简体   繁体   English

使用Stream从对象列表中查找最常见的属性值

[英]Find the most common attribute value from a List of objects using Stream

I have two classes that are structured like this: 我有两个结构如下的类:

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

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

Basically the Company class has a List of Person objects, and each Person object can get a Tag value. 基本上,Company类有一个Person对象列表,每个Person对象都可以获得Tag值。

If I get the List of the Person objects, is there a way to use Stream from Java 8 to find the one Tag value that is the most common among all the Person objects (in case of a tie, maybe just a random of the most common)? 如果我得到Person对象的List,有没有办法使用Java 8中的Stream来找到所有Person对象中最常见的一个Tag值(如果是平局,可能只是最随机的一个)共同)?

String mostCommonTag;
if(!company.getPerson().isEmpty) {
     mostCommonTag = company.getPerson().stream() //How to do this in Stream?
}
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);

AND the getTag method appeared twice, you can simplify the code as further: getTag方法出现了两次,可以进一步简化代码:

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

This should work for you: 这应该适合你:

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

The commonness map contains the information which tag was found how often. commonness图包含多久找到标签的信息。 The variable mostCommon contains the tag that was found most often. 变量mostCommon包含最常找到的标记。 Also, mostCommon is empty, if the original list was empty. 此外,如果原始列表为空,则mostCommon为空。

You could collect the counts to a Map, then get the key with the highest value 您可以将计数收集到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);

If you are open to using a third-party library, you can use Collectors2 from Eclipse Collections with a Java 8 Stream to create a Bag and request the topOccurrences , which will return a MutableList of ObjectIntPair which is the tag value and the count of the number of occurrences. 如果您愿意使用第三方库,则可以使用Eclipse Collections中的 Collectors2和Java 8 Stream来创建Bag并请求topOccurrences ,它将返回ObjectIntPairMutableList ,它是标记值和计数值。发生次数。

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

In the case of a tie, the MutableList will have more than one result. 在平局的情况下, MutableList将具有多个结果。

Note: I am a committer for Eclipse Collections. 注意:我是Eclipse Collections的提交者。

This is helpful for you, 这对你有帮助,

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

One More solution by AbacusUtil 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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM