使用Java Stream API从给定用户列表计算最常用姓氏的方法

问题描述 投票:2回答:4

函数应返回最常用姓氏的可选项(如果遇到至少两次)或者如果姓氏数相同或者用户列表为空则返回可选空

这是我提出的,但它不会返回Optional.empty

@Override
public Optional<String> getMostFrequentLastName(final List<User> users) {
            return users.stream()
                .map(User::getLastName)
                    .distinct()
                .collect
                        (Collectors.groupingBy(
                                Function.identity(),
                                Collectors.summingInt(w -> 1)
                        ))
                    .entrySet()
                    .stream()
                    .filter(stringIntegerEntry -> stringIntegerEntry.getValue() >= 2)
                    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                    .map(Map.Entry::getKey)
                    .findFirst();
}

这是我的测试课

public static void main(String[] args) {
    Optional<String> optionalS = Stream.of(new User("name1"),
             new User("name1"), new User("name2"), new User("name2"))
            .map(User::getLastName)
            .collect
                    (Collectors.groupingBy(
                            Function.identity(),
                            Collectors.counting()
                    ))
            .entrySet()
            .stream()
            .filter(stringIntegerEntry -> stringIntegerEntry.getValue() >= 2)
            .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .map(Map.Entry::getKey)
            .findFirst();
    System.out.println(optionalS.toString());
}

这是答案

Optional[name2]

但应该是

Optional[empty]
java lambda java-stream optional
4个回答
2
投票

你可以用

Optional<String> optionalS =
Stream.of(new User("name1"), new User("name1"), new User("name2"), new User("name2"))
        .collect(Collectors.groupingBy(User::getLastName, Collectors.counting()))
        .entrySet()
        .stream()
        .filter(entry -> entry.getValue() >= 2)
        .reduce((e1, e2) -> e1.getValue() < e2.getValue()? e2:
                            e1.getValue() > e2.getValue()? e1:
                            new AbstractMap.SimpleImmutableEntry<>(null, e1.getValue()))
        .map(Map.Entry::getKey);

System.out.println(optionalS.toString());

获得最大价值是减少的一种形式。因为你想在一个平局的情况下得到一个空的可选项,最简单的解决方案是明确地编写简化函数,如果有的话,使用值更大的Map.Entry,否则使用Map.Entry密钥构造一个新的null

减少的结果已经是一个Optional,如果没有元素(计数>=2)将是空的。所以最后的map步骤应用于Optional。如果已经为空,则不会评估map函数,并且生成的Optional保持为空。如果optional不是空的,但是Map.Entry::getKey计算为null,则生成的可选项将为空。


1
投票

在我看来,如果你有一些不同的lastNames的最大数量,你想要返回一个Optional::empty,如下:

Map<String, Long> map =
     Stream.of(new User("name1"),
               new User("name1"),
               new User("name2"),
               new User("name2"))
            .collect(Collectors.groupingBy(User::getLastName, Collectors.counting()));

map.entrySet()
   .stream()
   .max(Entry.comparingByValue())
   .flatMap(en -> {
           boolean b = map.entrySet()
                          .stream()
                          .filter(x -> !x.getKey().equals(en.getKey()))
                          .mapToLong(Entry::getValue)
                          .noneMatch(x -> x == en.getValue());
           return b ? Optional.of(en.getKey()) : Optional.empty();
       })
  .ifPresent(System.out::println);
}

0
投票

这里是我的怪物:

    Optional<String> optionalS = Stream.of(
            new User("name1"),
            new User("name1"),
            new User("name2"),
            new User("name2"))
            .map(User::getLastName)
            .collect(
                    Collectors.groupingBy(
                            Function.identity(),
                            Collectors.counting()
                    ))
            .entrySet()
            .stream()
            .filter(stringIntegerEntry -> stringIntegerEntry.getValue() >= 2)
            .collect(
                    Collectors.groupingBy(
                            Map.Entry::getValue,
                            Collectors.toList()
                    ))
            .entrySet()
            .stream()
            .sorted(Comparator.comparing(
                    Map.Entry::getKey,
                    Comparator.reverseOrder()))
            .map(Map.Entry::getValue)
            .findFirst()
            .filter(x -> x.size() == 1)
            .map(x -> x.get(0).getKey());

    System.out.println(optionalS);

0
投票

据我所知,你的代码创建的流解决方案

Map<String(lastname),Integer(number of occurence)> 

然后过滤那个出现次数> = 2的地图,在你的测试用例中你有一个带有条目的地图:

<"name1",2>
<"name2",2>

因此按值排序仍将返回两个值。

你应该尝试创造

Map<Integer,List<String>> 

这将存储出现次数 - >名称,然后过滤地图键,对它们进行降序排序,并且(在地图值中)您将获得最常见的姓氏(如果输入中有多次,则为姓氏)。

//编辑

以下简短代码片段与我的解决方案:

  Map<Integer, List<String>> map = new HashMap<>();
    map.put(2,Arrays.asList("name1","name2"));

    Optional<String> optionalS = map
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
            .findFirst() //get max{map's keys}
            .filter(x->x.getValue().size() == 1) //get lastname that occured only once
            .map(x->x.getValue().get(0)); //get that lastname (above filter check that list has only one element) or Optional.empty if stream didn't find any

    System.out.println(optionalS.toString());

我跳过了创建地图的部分。

附:您可以使用自定义比较器替换带有TreeMap的HashMap,以避免在流中进行排序。

© www.soinside.com 2019 - 2024. All rights reserved.