Группировать путем подсчета в Java 8 stream API
Я пытаюсь найти простой способ в API-интерфейсе Java 8 для выполнения группировки, я получаю этот сложный способ!
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));
System.out.println(collect);
List<String[]> collect2 = collect
.entrySet()
.stream()
.map(e -> new String[] { e.getKey(),
String.valueOf(e.getValue().size()) })
.collect(Collectors.toList());
collect2.forEach(o -> System.out.println(o[0] + " >> " + o[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
для большей эффективности. Что-то нужно помнить для вашего реального кода, если бы это было безопасно в вашем контексте.)
Ответ 2
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()));
Ответ 3
Вот несколько разных вариантов выполнения поставленной задачи.
используя 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));
Ответ 4
Вот пример для списка объектов
Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));
Ответ 5
Вот простое решение StreamEx
StreamEx.of(list).groupingBy(Function.identity(), Collectors.countingInt());
Уменьшите код шаблона: collect(Collectors.
Ответ 6
Если вы открыты для использования сторонней библиотеки, вы можете использовать класс Collectors2
в Eclipse Collections для преобразования List
в Bag
с помощью Stream
. 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
без использования 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");
Bag<String>
похож на Map<String, Integer>
в том, что он внутренне отслеживает ключи и их количество. Но, если вы спросите Map
о ключе, который в ней не содержится, он вернет null
. Если вы попросите Bag
для ключа, который он не содержит с помощью occurrencesOf
, он вернет 0.
Примечание: я являюсь коммиттером для Eclipse Collections.