Grupuj według liczenia w interfejsie API strumienia Java 8

170

Próbuję znaleźć prosty sposób w Java 8 stream API na grupowanie, wychodzę z tego złożonego sposobu!

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

Doceniam twój wkład.

Muhammad Hewedy
źródło
1
Co próbujesz tutaj osiągnąć?
Keppil
2
Jest to bardzo częsty przypadek, na przykład Wystąpił błąd w pewnym okresie i chcę zobaczyć statystyki dotyczące liczby wystąpień na każdy dzień w tym okresie.
Muhammad Hewedy

Odpowiedzi:

340

Myślę, że po prostu szukasz przeciążenia, które wymaga innego, Collectoraby określić, co zrobić z każdą grupą ... a następnie Collectors.counting()policzyć:

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

Wynik:

{Hello=2, World=1}

(Istnieje również możliwość użycia w groupingByConcurrentcelu zwiększenia wydajności. Coś, o czym należy pamiętać w przypadku prawdziwego kodu, jeśli byłoby to bezpieczne w Twoim kontekście).

Jon Skeet
źródło
1
Idealny! ... from javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy
6
Użycie funkcji Function.identity () (ze statycznym importem) zamiast e -> e sprawia, że ​​czytanie jest trochę przyjemniejsze: Map <String, Long> counted = list.stream (). Collect (groupingBy (identity (), counting ()) ));
Kuchi
Cześć, zastanawiałem się, czy ktoś mógłby wyjaśnić aspekt mapy w kodzie Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));, co dokładnie dzieje się w tym miejscu i wszelkie linki z dalszymi wyjaśnieniami związanymi z tematem, które można przesłać
Puste
@Blank: To czuje się jak to będzie najlepiej jako nowe pytanie, z ty wyjaśniając, które części to ty nie rozumiesz pierwszy. Przejrzenie każdego aspektu (nie wiedząc, którego fragmentu nie rozumiesz) zajęłoby bardzo dużo czasu - więcej czasu niż jestem gotów poświęcić na odpowiedź, która ma ponad 5 lat w tym momencie, kiedy większość z nich ty może już rozumiem.
Jon Skeet
@JonSkeet Cool, postawię to w nowym pytaniu, chociaż podkreśliłem aspekt, którego nie rozumiem w moim pytaniu. To jest cały fragment kodu, który dodałem wraz z nim.
Puste
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()));
Sivakumar
źródło
8

Oto przykład listy obiektów

Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));
fjkjava
źródło
8

Oto nieco inne opcje wykonania zadania.

używając toMap:

list.stream()
    .collect(Collectors.toMap(Function.identity(), e -> 1, Math::addExact));

używając Map::merge:

Map<String, Integer> accumulator = new HashMap<>();
list.forEach(s -> accumulator.merge(s, 1, Math::addExact));
Ousmane D.
źródło
4

Oto proste rozwiązanie firmy StreamEx

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

Zmniejsz kod standardowy: collect(Collectors.

user_3380739
źródło
1
Jaki jest powód używania go w przypadku strumieni Java8?
Torsten Ojaperv,
1

Jeśli jesteś otwarty na korzystanie z biblioteki innej firmy, możesz użyć Collectors2klasy w kolekcji Eclipse, aby przekonwertować Listplik na plik Bagprzy użyciu pliku Stream. A Bagto struktura danych stworzona do liczenia .

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

Wynik:

{World=1, Hello=2}

W tym konkretnym przypadku można po prostu się bezpośrednio na .collectListBag

Bag<String> counted = 
        list.stream().collect(Collectors2.toBag());

Możesz również utworzyć Bagplik bez użycia a Stream, dostosowując go Listza pomocą protokołów Eclipse Collections.

Bag<String> counted = Lists.adapt(list).countBy(each -> each);

lub w tym konkretnym przypadku:

Bag<String> counted = Lists.adapt(list).toBag();

Możesz także po prostu utworzyć torbę bezpośrednio.

Bag<String> counted = Bags.mutable.with("Hello", "Hello", "World");

A Bag<String>jest jak a Map<String, Integer>, ponieważ wewnętrznie śledzi klucze i ich liczbę. Ale jeśli poprosisz Mapo klucz, którego nie zawiera, zwróci null. Jeśli poprosisz Bago klucz, którego nie zawiera occurrencesOf, zwróci 0.

Uwaga: jestem promotorem Eclipse Collections.

Donald Raab
źródło