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

Method to calculate the most frequent last name from list of given users with Java Stream API

函数应该return可选最频繁的姓氏(如果至少遇到两次)或者如果姓氏数量相同或用户列表为空则可选为空

这是我想出来的,但不是 return 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();
}

这是我的测试class

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());
}

Here is the awnser

Optional[name2]

但应该是

Optional[empty]

据我了解您在流中的解决方案是您的代码创建的

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

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

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

因此按值排序仍将 return 两个值。

你应该尝试创建

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());

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

P.S。您可以使用自定义比较器将 HashMap 替换为 TreeMap 以避免在流中排序。

这是我的怪物:

    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);

在我看来,如果你有相同数量的最大的一些不同的姓氏,你想要return一个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);
}

您可以使用

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.Entrynull 键。

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