Viviana, parte de este gran equipo, nos ha hecho el favor de redactar esta reseña porque domina a la perfección el tema.
Quiero compartir la solución que encontré porque al principio esperaba usar métodos de mapa y reducción, pero fue un poco diferente.
Map collect =
wordsList.stream().collect(groupingBy(Function.identity(), counting()));
O para valores enteros:
Map collect =
wordsList.stream().collect(groupingBy(Function.identity(), summingInt(e -> 1)));
EDITAR
Añado cómo ordenar el mapa por valor:
LinkedHashMap 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
));
(NOTA: Vea las ediciones a continuación.)
Como alternativa a la respuesta de Mounas, aquí hay un enfoque que cuenta las palabras en paralelo:
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 list = Arrays.asList(
"hello", "bye", "ciao", "bye", "ciao");
Map counts = list.parallelStream().
collect(Collectors.toConcurrentMap(
w -> w, w -> 1, Integer::sum));
System.out.println(counts);
EDITAR En respuesta al comentario, realicé una pequeña prueba con JMH, comparando el
toConcurrentMap
y elgroupingByConcurrent
enfoque, con diferentes tamaños de lista de entrada y palabras aleatorias de diferentes longitudes. Esta prueba sugirió que eltoConcurrentMap
El acercamiento fue más rápido. Al considerar cuán diferentes son estos enfoques “bajo el capó”, es difícil predecir algo como esto.Como una extensión adicional, basada en comentarios adicionales, extendí la prueba para cubrir las cuatro combinaciones de
toMap
,groupingBy
, serial y paralelo.Los resultados siguen siendo que
toMap
El enfoque es más rápido, pero inesperadamente (al menos para mí) las versiones “concurrentes” en ambos casos son más lentas que las versiones en serie …:
(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
No tengo tanta experiencia con JMH, tal vez hice algo mal aquí; las sugerencias y correcciones son bienvenidas:
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 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 counts =
list.stream().collect(
Collectors.toMap(
w -> w, w -> 1, Integer::sum));
bh.consume(counts);
else if (method.equals("toConcurrentMap"))
Map counts =
list.parallelStream().collect(
Collectors.toConcurrentMap(
w -> w, w -> 1, Integer::sum));
bh.consume(counts);
else if (method.equals("groupingBy"))
Map counts =
list.stream().collect(
Collectors.groupingBy(
Function.identity(), Collectors.counting()));
bh.consume(counts);
else if (method.equals("groupingByConcurrent"))
Map counts =
list.parallelStream().collect(
Collectors.groupingByConcurrent(
Function.identity(), Collectors. 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 createRandomStrings(
int count, int length, Random random)
List list = new ArrayList(count);
for (int i = 0; i < count; i++)
list.add(createRandomString(length, random));
return list;
Los tiempos son sólo similares para el caso de serie de una lista con 10000 elementos y palabras de 2 letras.
Podría valer la pena comprobar si para tamaños de lista aún más grandes, las versiones simultáneas eventualmente superan a las seriales, pero actualmente no tienen tiempo para otra ejecución de referencia detallada con todas estas configuraciones.
Encuentre el artículo más frecuente en la colección, con genéricos:
private V findMostFrequentItem(final Collection 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);
Calcule las frecuencias de los artículos:
private Map findFrequencies(final Collection items)
return items.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));