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}
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
));
(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
undgroupingByConcurrent
mit verschiedenen Eingangslistengrößen und Zufallswörtern unterschiedlicher Länge. Dieser Test deutete darauf hin, dass dietoConcurrentMap
-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.
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()));
}
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
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 $
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)));
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)
);
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);
}
}