#java #loops #arraylist #count #compare
#java #функциональное программирование #java-8
Вопрос:
Я хочу собрать элементы в потоке в карту, которая группирует равные объекты вместе и сопоставляет количество вхождений.
List<String> list = Arrays.asList("Hello", "Hello", "World");
Map<String, Long> wordToFrequency = // what goes here?
Итак, в этом случае я бы хотел, чтобы карта состояла из этих записей:
Hello -> 2
World -> 1
Как я могу это сделать?
Ответ №1:
Я думаю, вы просто ищете перегрузку, которая требует другого Collector
, чтобы указать, что делать с каждой группой … а затем Collectors.counting()
выполнить подсчет:
import java.util.*;
import java.util.stream.*;
class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("World");
Map<String, Long> counted = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(counted);
}
}
Результат:
{Hello=2, World=1}
(Существует также возможность использования groupingByConcurrent
для большей эффективности. Что-то, что нужно иметь в виду для вашего реального кода, если это будет безопасно в вашем контексте.)
Комментарии:
1. Отлично! … из javadoc
and then performing a reduction operation on the values associated with a given key using the specified downstream Collector
2. Использование Function.identity() (со статическим импортом) вместо e -> e делает чтение немного приятнее: Map<String, Long> counted = list.stream().collect(groupingBy(identity(), counting()));
3. Здравствуйте, у меня есть еще один вопрос, что, если я хочу показать его в порядке убывания? Если у меня есть 4 мира и 2 привета и что им показать {World = 4, Hello = 2}
4. @MichaelKors: Если у вас есть другой вопрос, вы должны задать его в виде отдельного сообщения после проведения соответствующих исследований.
Ответ №2:
Вот пример для списка объектов
Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));
Ответ №3:
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("World");
Map<String, List<String>> collect = list.stream()
.collect(Collectors.groupingBy(o -> o));
collect.entrySet()
.forEach(e -> System.out.println(e.getKey() " - " e.getValue().size()));
Ответ №4:
Вот несколько другие варианты для выполнения поставленной задачи.
используя toMap
:
list.stream()
.collect(Collectors.toMap(Function.identity(), e -> 1, Math::addExact));
используя Map::merge
:
Map<String, Integer> accumulator = new HashMap<>();
list.forEach(s -> accumulator.merge(s, 1, Math::addExact));
Ответ №5:
Вот простое решение от StreamEx:
StreamEx.of(list).groupingBy(Function.identity(), MoreCollectors.countingInt());
Преимущество этого заключается в сокращении шаблонного кода Java stream: collect(Collectors.
Комментарии:
1. В чем причина использования его поверх потоков Java8?
2. @TorstenOjaperv Единственная реальная причина в том, что он более лаконичен (это уменьшает шаблонность).
Ответ №6:
Если вы открыты для использования сторонней библиотеки, вы можете использовать Collectors2
класс в Eclipse Collections для преобразования List
в a Bag
с помощью a Stream
. A Bag
— это структура данных, созданная для подсчета.
Bag<String> counted =
list.stream().collect(Collectors2.countBy(each -> each));
Assert.assertEquals(1, counted.occurrencesOf("World"));
Assert.assertEquals(2, counted.occurrencesOf("Hello"));
System.out.println(counted.toStringOfItemToCount());
Вывод:
{World=1, Hello=2}
В этом конкретном случае вы можете просто collect
List
перейти непосредственно в Bag
.
Bag<String> counted =
list.stream().collect(Collectors2.toBag());
Вы также можете создать Bag
без использования a Stream
, адаптировав List
его с помощью протоколов Eclipse Collections.
Bag<String> counted = Lists.adapt(list).countBy(each -> each);
или в данном конкретном случае:
Bag<String> counted = Lists.adapt(list).toBag();
Вы также можете просто создать пакет напрямую.
Bag<String> counted = Bags.mutable.with("Hello", "Hello", "World");
A Bag<String>
похож на a Map<String, Integer>
в том, что он внутренне отслеживает ключи и их количество. Но, если вы Map
попросите a указать ключ, которого он не содержит, он вернется null
. Если вы запросите Bag
у a ключ, который он не содержит using occurrencesOf
, он вернет 0 .
Примечание: я являюсь коммиттером для коллекций Eclipse.