Найдите наиболее распространенное значение атрибута из списка объектов, используя 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();