Cách đếm tần suất của các từ trong Danh sách trong Java 8?Số tần số từ Java 8
List <String> = Lists.newArrayList("hello", "bye", "ciao", "bye", "ciao");
Kết quả phải:
{ciao=2, hello=1, bye=2}
Cách đếm tần suất của các từ trong Danh sách trong Java 8?Số tần số từ Java 8
List <String> = Lists.newArrayList("hello", "bye", "ciao", "bye", "ciao");
Kết quả phải:
{ciao=2, hello=1, bye=2}
tôi muốn chia sẻ những giải pháp tôi thấy vì lúc đầu tôi dự kiến sẽ sử dụng bản đồ-and-giảm phương pháp, nhưng đó là một chút khác nhau.
Map<String, Long> collect =
wordsList.stream().collect(groupingBy(Function.identity(), counting()));
Hoặc cho các giá trị Integer:
Map<String, Integer> collect =
wordsList.stream().collect(groupingBy(Function.identity(), summingInt(e -> 1)));
EDIT
tôi thêm làm thế nào để sắp xếp các bản đồ theo giá trị:
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
));
(LƯU Ý: Xem các chỉnh sửa bên dưới)
Để thay thế cho Mounas answer, đây là một cách tiếp cận mà từ đếm song song:
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);
}
}
EDIT Để đối phó với những nhận xét, tôi chạy một thử nghiệm nhỏ với JMH, so sánh
toConcurrentMap
và cách tiếp cậngroupingByConcurrent
, với các kích thước danh sách đầu vào khác nhau và các từ ngẫu nhiên có độ dài khác nhau. Thử nghiệm này cho thấy cách tiếp cậntoConcurrentMap
nhanh hơn. Khi xem xét các cách tiếp cận này khác nhau như thế nào "dưới mui xe", thật khó để dự đoán một cái gì đó như thế này.Là phần mở rộng hơn nữa, dựa trên các nhận xét thêm, tôi đã mở rộng thử nghiệm để bao gồm tất cả bốn kết hợp của
toMap
,groupingBy
, nối tiếp và song song.Kết quả vẫn còn đó cách tiếp cận
toMap
là nhanh hơn, nhưng bất ngờ (ít nhất, đối với tôi) các phiên bản "đồng thời" trong cả hai trường hợp là chậm hơn so với các phiên bản nối tiếp ...:
(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
tôi không quá kinh nghiệm với JMH, có lẽ tôi đã làm điều gì sai ở đây - đề xuất và sửa được chào đón:
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;
}
}
giờ chỉ tương tự như đối với trường hợp nối tiếp của một danh sách với 10000 yếu tố, và 2 chữ cái. Có thể đáng để kiểm tra xem liệu các kích thước danh sách lớn hơn hay không, các phiên bản đồng thời cuối cùng cũng hoạt động tốt hơn các phiên bản nối tiếp, nhưng hiện tại không có thời gian cho một điểm chuẩn chi tiết khác với tất cả các cấu hình này.
Tôi đoán, 'Collectors.groupingByConcurrent (w-> w, Collectors.counting())' sẽ hiệu quả hơn. – Holger
@Holger Tôi đã thêm một EDIT về điều này. – Marco13
Bạn cũng nên quan tâm đến số từ * bằng *. Sự tranh chấp cho một mục nhập bản đồ có thể có tác động đáng kể. Việc đếm hàng ngàn từ riêng biệt có thể hoạt động mà không có bất kỳ tranh chấp nào trong 'Bản đồ đồng thời' của Java 8 mặc dù tôi sẽ không gọi lưu trữ '1' "đếm". Vì vậy, đếm hàng ngàn lần xuất hiện của cùng một từ có thể đưa ra một hình ảnh khác nhau… – Holger
Nếu bạn sử dụng Eclipse Collections, bạn chỉ có thể chuyển đổi List
thành Bag
.
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"));
Mã này sẽ hoạt động với Java 5 - 8.
Lưu ý: Tôi là một người có duyên cho Eclipse Collections
tôi sẽ trình bày các giải pháp ở đây mà tôi đã thực hiện (một với nhóm là tốt hơn nhiều :)).
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)));
}
Chỉ cần tôi 0.02 $
Tôi đã biết về Collections.frequency (đầu vào, str). Cảm ơn về thông tin bạn vừa nhập. – Sam
Một 2 phần trăm của tôi, đưa ra một mảng:
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)));
Đây là một cách để tạo ra một bản đồ tần số sử dụng chức năng bản đồ.
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}
Hoặc
words.forEach(word ->
frequencyMap.compute(word, (k, v) -> v != null ? v + 1 : 1)
);
Tìm mục thường xuyên nhất trong bộ sưu tập, với Generics:
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);
}
Tính mục tần số:
private <V> Map<V, Long> findFrequencies(final Collection<V> items)
{
return items.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Functions.identity(), Collectors.counting()));
}
Tại sao 'summingInt (e -> 1) '? Chỉ cần sử dụng ['counting()'] (http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#counting--). Kết quả sẽ là một 'Bản đồ 'sau đó nhưng điều đó sẽ không bị tổn thương. Btw. bạn có thể thay thế 'e-> e' bằng' Function.identity() '(nhưng bạn không phải). –
Holger