【问题标题】:Find the most common attribute value from a List of objects using Stream使用 Stream 从对象列表中查找最常见的属性值
【发布时间】:2017-09-22 19:22:40
【问题描述】:

我有两个类的结构如下:

public class Company {
     private List<Person> person;
     ...
     public List<Person> getPerson() {
          return person;
     }
     ...
}

public class Person {
     private String tag;
     ...
     public String getTag() {
          return tag;
     }
     ...
}

基本上Company类有一个Person对象的List,每个Person对象都可以得到一个Tag值。

如果我得到 Person 对象的列表,有没有办法使用 Java 8 中的 Stream 来找到所有 Person 对象中最常见的一个 Tag 值(如果是平局,可能只是随机的最常见的)?

String mostCommonTag;
if(!company.getPerson().isEmpty) {
     mostCommonTag = company.getPerson().stream() //How to do this in Stream?
}

【问题讨论】:

  • 您也可以使用Stream 获得该公司最常用的标签Company 中的Map。此外,不需要!company.getPerson().isEmpty() 等。

标签: java list java-8 java-stream


【解决方案1】:
String mostCommonTag = getPerson().stream()
        // filter some person without a tag out 
        .filter(it -> Objects.nonNull(it.getTag()))
        // summarize tags
        .collect(Collectors.groupingBy(Person::getTag, Collectors.counting()))
        // fetch the max entry
        .entrySet().stream().max(Map.Entry.comparingByValue())
        // map to tag
        .map(Map.Entry::getKey).orElse(null);

ANDgetTag方法出现了两次,可以进一步简化代码:

String mostCommonTag = getPerson().stream()
        // map person to tag & filter null tag out 
        .map(Person::getTag).filter(Objects::nonNull)
        // summarize tags
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        // fetch the max entry
        .entrySet().stream().max(Map.Entry.comparingByValue())
        // map to tag
        .map(Map.Entry::getKey).orElse(null);

【讨论】:

  • 感谢您的详细回答。不过,有一个澄清,我得到一个“元素不能映射到空键”异常,因为显然有些人没有标签值。我应该如何在 Stream 中处理这个问题。
【解决方案2】:

您可以将计数收集到 Map,然后获取具有最高值的键

List<String> foo = Arrays.asList("a","b","c","d","e","e","e","f","f","f","g");
Map<String, Long> f = foo
    .stream()
    .collect(Collectors.groupingBy(v -> v, Collectors.counting()));
String maxOccurence = 
            Collections.max(f.entrySet(), Comparator.comparing(Map.Entry::getValue)).getKey();

System.out.println(maxOccurence);

【讨论】:

    【解决方案3】:

    这应该适合你:

    private void run() {
        List<Person> list = Arrays.asList(() -> "foo", () -> "foo", () -> "foo",
                                          () -> "bar", () -> "bar");
        Map<String, Long> commonness = list.stream()
                .collect(Collectors.groupingBy(Person::getTag, Collectors.counting()));
        Optional<String> mostCommon = commonness.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey);
        System.out.println(mostCommon.orElse("no elements in list"));
    }
    
    public interface Person {
        String getTag();
    }
    

    commonness 映射包含发现标签的频率信息。变量mostCommon 包含最常被发现的标签。此外,如果原始列表为空,mostCommon 为空。

    【讨论】:

      【解决方案4】:

      如果您愿意使用第三方库,您可以使用来自Eclipse CollectionsCollectors2 和Java 8 Stream 创建一个Bag 并请求topOccurrences,这将返回一个@ 987654327@ of ObjectIntPair 是标签值和出现次数的计数。

      MutableList<ObjectIntPair<String>> topOccurrences = company.getPerson()
              .stream()
              .map(Person::getTag)
              .collect(Collectors2.toBag())
              .topOccurrences(1);
      String mostCommonTag = topOccurrences.getFirst().getOne();
      

      在平局的情况下,MutableList 将有多个结果。

      注意:我是 Eclipse Collections 的提交者。

      【讨论】:

        【解决方案5】:

        这对你有帮助,

        Map<String, Long> count = persons.stream().collect(
                        Collectors.groupingBy(Person::getTag, Collectors.counting()));
        
        Optional<Entry<String, Long>> maxValue = count .entrySet()
                .stream().max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1).get().getKey();
        
        maxValue.get().getValue();
        

        【讨论】:

        • 这个比较器不处理相等 - 虽然这可能适用于 max,但这样做通常不是一个好主意 - 只需使用众多方法中的一种来获得合适的 Comparator,例如通过Comparator.comparingLongLong::compareToMap.Entry.comparingByValue
        【解决方案6】:

        AbacusUtil 提供的另一个解决方案

        // Comparing the solution by jdk stream, 
        // there is no "collect(Collectors.groupingBy(Person::getTag, Collectors.counting())).entrySet().stream"
        Stream.of(company.getPerson()).map(Person::getTag).skipNull() //
                .groupBy(Fn.identity(), Collectors.counting()) //
                .max(Comparators.comparingByValue()).map(e -> e.getKey()).orNull();
        
        // Or by multiset
        Stream.of(company.getPerson()).map(Person::getTag).skipNull() //
                .toMultiset().maxOccurrences().map(e -> e.getKey()).orNull();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-09-14
          • 2018-11-03
          • 1970-01-01
          • 1970-01-01
          • 2023-04-03
          • 2018-07-09
          • 2023-03-16
          • 1970-01-01
          相关资源
          最近更新 更多