Найдите наиболее распространенное значение атрибута из списка объектов, используя Stream

У меня есть два класса, которые структурированы следующим образом:

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 есть объекты List of Person, и каждый объект Person может получить значение тега.

Если я получаю объекты List of Person, есть способ использовать Stream из Java 8, чтобы найти одно значение тега, которое является наиболее распространенным среди всех объектов Person (в случае привязки, может быть, просто случайный из наиболее распространенных)?

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

И метод 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);

Ответ 2

Это должно сработать для вас:

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 пуст, если исходный список пуст.

Ответ 3

Вы можете собрать подсчеты на карту, а затем получить ключ с наивысшим значением

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

Ответ 4

Если вы открыты для использования сторонней библиотеки, вы можете использовать Collectors2 из Eclipse Collections с Java 8 Stream для создания Bag и запросить topOccurrences, который вернет MutableList из ObjectIntPair, который является значением тега, и счет количества вхождений.

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

В случае привязки MutableList будет иметь более одного результата.

Примечание. Я являюсь коммиттером для коллекций Eclipse.

Ответ 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();

Ответ 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();