webentwicklung-frage-antwort-db.com.de

Worthäufigkeitszählung Java 8

Wie zählt man die Häufigkeit der Wörter von List in Java 8?

List <String> wordsList = Lists.newArrayList("hello", "bye", "ciao", "bye", "ciao");

Das Ergebnis muss sein: 

{ciao=2, hello=1, bye=2}
49
Mouna

Ich möchte die Lösung teilen, die ich gefunden habe, weil ich zunächst erwartet hatte, Karten- und Verkleinerungsmethoden zu verwenden, aber es war ein bisschen anders.

Map<String, Long> collect = 
        wordsList.stream().collect(groupingBy(Function.identity(), counting()));

Oder für Integer-Werte:

Map<String, Integer> collect = 
        wordsList.stream().collect(groupingBy(Function.identity(), summingInt(e -> 1)));

EDIT

Ich füge hinzu, wie die Karte nach Wert sortiert wird: 

LinkedHashMap<String, Long> countByWordSorted = collect.entrySet()
            .stream()
            .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue,
                    (v1, v2) -> {
                        throw new IllegalStateException();
                    },
                    LinkedHashMap::new
            ));
73
Mouna

(HINWEIS: Siehe die Änderungen unter)

Als Alternative zu Mounas Antwort ist hier ein Ansatz, bei dem das Wort parallel zählt:

import Java.util.Arrays;
import Java.util.List;
import Java.util.Map;
import Java.util.stream.Collectors;

public class ParallelWordCount
{
    public static void main(String[] args)
    {
        List<String> list = Arrays.asList(
            "hello", "bye", "ciao", "bye", "ciao");
        Map<String, Integer> counts = list.parallelStream().
            collect(Collectors.toConcurrentMap(
                w -> w, w -> 1, Integer::sum));
        System.out.println(counts);
    }
}

BEARBEITEN Als Antwort auf den Kommentar führte ich einen kleinen Test mit JMH durch und verglich die Ansätze toConcurrentMap und groupingByConcurrent mit verschiedenen Eingangslistengrößen und Zufallswörtern unterschiedlicher Länge. Dieser Test deutete darauf hin, dass die toConcurrentMap-Methode schneller war. Wenn man bedenkt, wie unterschiedlich diese Ansätze "unter der Haube" sind, kann man so etwas schwer vorhersagen.

Als weitere Erweiterung, basierend auf weiteren Kommentaren, erweiterte ich den Test auf alle vier Kombinationen von toMap, groupingBy, seriell und parallel. 

Das Ergebnis ist immer noch, dass die toMap-Methode schneller ist, aber unerwartet (zumindest für mich) sind die "gleichzeitigen" Versionen in beiden Fällen langsamer als die seriellen Versionen ...:

             (method)  (count) (wordLength)  Mode  Cnt     Score    Error  Units
      toConcurrentMap     1000            2  avgt   50   146,636 ±  0,880  us/op
      toConcurrentMap     1000            5  avgt   50   272,762 ±  1,232  us/op
      toConcurrentMap     1000           10  avgt   50   271,121 ±  1,125  us/op
                toMap     1000            2  avgt   50    44,396 ±  0,541  us/op
                toMap     1000            5  avgt   50    46,938 ±  0,872  us/op
                toMap     1000           10  avgt   50    46,180 ±  0,557  us/op
           groupingBy     1000            2  avgt   50    46,797 ±  1,181  us/op
           groupingBy     1000            5  avgt   50    68,992 ±  1,537  us/op
           groupingBy     1000           10  avgt   50    68,636 ±  1,349  us/op
 groupingByConcurrent     1000            2  avgt   50   231,458 ±  0,658  us/op
 groupingByConcurrent     1000            5  avgt   50   438,975 ±  1,591  us/op
 groupingByConcurrent     1000           10  avgt   50   437,765 ±  1,139  us/op
      toConcurrentMap    10000            2  avgt   50   712,113 ±  6,340  us/op
      toConcurrentMap    10000            5  avgt   50  1809,356 ±  9,344  us/op
      toConcurrentMap    10000           10  avgt   50  1813,814 ± 16,190  us/op
                toMap    10000            2  avgt   50   341,004 ± 16,074  us/op
                toMap    10000            5  avgt   50   535,122 ± 24,674  us/op
                toMap    10000           10  avgt   50   511,186 ±  3,444  us/op
           groupingBy    10000            2  avgt   50   340,984 ±  6,235  us/op
           groupingBy    10000            5  avgt   50   708,553 ±  6,369  us/op
           groupingBy    10000           10  avgt   50   712,858 ± 10,248  us/op
 groupingByConcurrent    10000            2  avgt   50   901,842 ±  8,685  us/op
 groupingByConcurrent    10000            5  avgt   50  3762,478 ± 21,408  us/op
 groupingByConcurrent    10000           10  avgt   50  3795,530 ± 32,096  us/op

Ich habe mit JMH nicht so viel Erfahrung, vielleicht habe ich hier etwas falsch gemacht - Vorschläge und Korrekturen sind erwünscht:

import Java.util.ArrayList;
import Java.util.List;
import Java.util.Map;
import Java.util.Random;
import Java.util.concurrent.TimeUnit;
import Java.util.function.Function;
import Java.util.stream.Collectors;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Thread)
public class ParallelWordCount
{

    @Param({"toConcurrentMap", "toMap", "groupingBy", "groupingByConcurrent"})
    public String method;

    @Param({"2", "5", "10"})
    public int wordLength;

    @Param({"1000", "10000" })
    public int count;

    private List<String> list;

    @Setup
    public void initList()
    {
         list = createRandomStrings(count, wordLength, new Random(0));
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public void testMethod(Blackhole bh)
    {

        if (method.equals("toMap"))
        {
            Map<String, Integer> counts =
                list.stream().collect(
                    Collectors.toMap(
                        w -> w, w -> 1, Integer::sum));
            bh.consume(counts);
        }
        else if (method.equals("toConcurrentMap"))
        {
            Map<String, Integer> counts =
                list.parallelStream().collect(
                    Collectors.toConcurrentMap(
                        w -> w, w -> 1, Integer::sum));
            bh.consume(counts);
        }
        else if (method.equals("groupingBy"))
        {
            Map<String, Long> counts =
                list.stream().collect(
                    Collectors.groupingBy(
                        Function.identity(), Collectors.<String>counting()));
            bh.consume(counts);
        }
        else if (method.equals("groupingByConcurrent"))
        {
            Map<String, Long> counts =
                list.parallelStream().collect(
                    Collectors.groupingByConcurrent(
                        Function.identity(), Collectors.<String> counting()));
            bh.consume(counts);
        }
    }

    private static String createRandomString(int length, Random random)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++)
        {
            int c = random.nextInt(26);
            sb.append((char) (c + 'a'));
        }
        return sb.toString();
    }

    private static List<String> createRandomStrings(
        int count, int length, Random random)
    {
        List<String> list = new ArrayList<String>(count);
        for (int i = 0; i < count; i++)
        {
            list.add(createRandomString(length, random));
        }
        return list;
    }
}

Die Zeiten sind nur für den Serienfall einer Liste mit 10000 Elementen und Wörtern mit zwei Buchstaben ähnlich. 

Es könnte sich lohnen, zu prüfen, ob bei noch größeren Listengrößen die gleichzeitigen Versionen letztendlich die seriellen Versionen übertreffen, aber derzeit nicht die Zeit für einen weiteren detaillierten Benchmark-Lauf mit all diesen Konfigurationen hat. 

25
Marco13

Finden Sie das häufigste Element in der Sammlung mit Generika:

private <V> V findMostFrequentItem(final Collection<V> items)
{
  return items.stream()
      .filter(Objects::nonNull)
      .collect(Collectors.groupingBy(Functions.identity(), Collectors.counting()))
      .entrySet()
      .stream()
      .max(Comparator.comparing(Entry::getValue))
      .map(Entry::getKey)
      .orElse(null);
}

Artikelhäufigkeiten berechnen:

private <V> Map<V, Long> findFrequencies(final Collection<V> items)
{
  return items.stream()
      .filter(Objects::nonNull)
      .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
4
nejckorasa

Wenn Sie Eclipse Collections verwenden, können Sie die List einfach in eine Bag konvertieren.

Bag<String> words = Lists.mutable.with("hello", "bye", "ciao", "bye", "ciao").toBag();
Assert.assertEquals(2, words.occurrencesOf("ciao"));
Assert.assertEquals(1, words.occurrencesOf("hello"));
Assert.assertEquals(2, words.occurrencesOf("bye"));

Dieser Code funktioniert mit Java 5 - 8.

Hinweis: Ich bin ein Committer für Eclipse Collections

3
Donald Raab

Ich werde hier die Lösung vorstellen, die ich gemacht habe (die mit Gruppierung ist viel besser :)).

static private void test0(List<String> input) {
    Set<String> set = input.stream()
            .collect(Collectors.toSet());
    set.stream()
            .collect(Collectors.toMap(Function.identity(),
                    str -> Collections.frequency(input, str)));
}

Nur meine 0,02 $

2
Eugene

Weitere 2 Cent von mir bei gegebenem Array:

import static Java.util.stream.Collectors.*;

String[] str = {"hello", "bye", "ciao", "bye", "ciao"};    
Map<String, Integer> collected 
= Arrays.stream(str)
        .collect(groupingBy(Function.identity(), 
                    collectingAndThen(counting(), Long::intValue)));
0
Sym-Sym

So erstellen Sie eine Frequenzkarte mit Kartenfunktionen.

List<String> words = Stream.of("hello", "bye", "ciao", "bye", "ciao").collect(toList());
Map<String, Integer> frequencyMap = new HashMap<>();

words.forEach(Word ->
        frequencyMap.merge(Word, 1, (v, newV) -> v + newV)
);

System.out.println(frequencyMap); // {ciao=2, hello=1, bye=2}

Oder

words.forEach(Word ->
       frequencyMap.compute(Word, (k, v) -> v != null ? v + 1 : 1)
);
0
Piyush
public class Main {

    public static void main(String[] args) {


        String testString ="qqwweerrttyyaaaaaasdfasafsdfadsfadsewfywqtedywqtdfewyfdweytfdywfdyrewfdyewrefdyewdyfwhxvsahxvfwytfx"; 
        long Java8Case2 = testString.codePoints().filter(ch -> ch =='a').count();
        System.out.println(Java8Case2);

        ArrayList<Character> list = new ArrayList<Character>();
        for (char c : testString.toCharArray()) {
          list.add(c);
        }
        Map<Object, Integer> counts = list.parallelStream().
            collect(Collectors.toConcurrentMap(
                w -> w, w -> 1, Integer::sum));
        System.out.println(counts);
    }

}
0
Easycoder