Згрупуйте, рахуючи в потоці API 8


170

Я намагаюся знайти простий спосіб в Java 8 stream API зробити групування, я виходжу цим складним способом!

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
Що ви намагаєтеся досягти тут?
Keppil

2
Це дуже поширений випадок, наприклад, у мене помилка трапляється за певний проміжок часу, і я хочу бачити деякі статистичні дані про кількість випадкових випадків на кожен день у цей проміжок часу.
Muhammad Hewedy

Відповіді:


340

Я думаю, ти просто шукаєш перевантаження, яке потребує іншого, 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
Ідеально! ... від javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy

6
Використання Function.identity () (зі статичним імпортом) замість e -> e робить його трохи приємніше читати: Map <String, Long> counted = list.stream (). Collection (groupBy (identitet (), counting () ));
Кучі

Привіт, мені було цікаво, чи хтось може пояснити карту аспекту коду Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));, що саме відбувається в цей момент та будь-які посилання з подальшим поясненням, пов’язаним із темою, яку можна надсилати
Пустий

@Blank: Це відчуває , як це було б краще всього, як нове питання, з вами пояснюючи , які його частини ви дійсно розумієте перший. Перегляд кожного його аспекту (не знаючи, який біт ви не розумієте) зайняв би дуже багато часу - більше часу, ніж я готовий дати відповідь, якій зараз вже більше 5 років, коли більшість із вас може вже зрозуміти.
Джон Скіт

@JonSkeet Cool, я поставлю це нове запитання, хоча я виділив аспект, якого я не зрозумів у своєму питанні. Це, цілий фрагмент коду, який я додав разом з ним.
Пустий

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

8

Ось приклад для списку об’єктів

Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));

8

Ось дещо інші варіанти виконання завдання, що знаходиться під рукою.

використовуючи 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

Ось просте рішення від StreamEx

StreamEx.of(list).groupingBy(Function.identity(), Collectors.countingInt());

Зменшіть код котла: collect(Collectors.


1
Яка причина використовувати його над потоками Java8?
Torsten Ojaperv

1

Якщо ви відкриті для використання бібліотеки сторонніх виробників , ви можете використовувати Collectors2клас в Eclipse , Collections перетворити Listдо Bagвикористовуючи 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}

В даному конкретному випадку, ви можете просто безпосередньо в .collectListBag

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

A Bag<String>- це як Map<String, Integer>те, що він внутрішньо відстежує ключі та їх кількість. Але якщо ви попросите Mapключ, який він не містить, він повернеться null. Якщо ви запитаєте Bagключ, який він не містить occurrencesOf, він поверне 0.

Примітка. Я є членом колекції Eclipse.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.