Nhóm bằng cách đếm trong API luồng Java 8


170

Tôi cố gắng tìm một cách đơn giản trong API luồng Java 8 để thực hiện việc nhóm, tôi đi ra với cách phức tạp này!

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

Tôi đánh giá cao đầu vào của bạn.


1
Bạn đang cố gắng để đạt được điều gì ở đây?
Keppil

2
Đây là một trường hợp rất phổ biến, ví dụ tôi có lỗi xảy ra trong một khoảng thời gian và tôi muốn xem một số thống kê về số lần xuất hiện mỗi ngày trong khoảng thời gian này.
Muhammad Hewedy

Câu trả lời:


339

Tôi nghĩ rằng bạn chỉ đang tìm kiếm sự quá tải cần một người khác Collectorđể chỉ định phải làm gì với từng nhóm ... và sau đó Collectors.counting()thực hiện việc đếm:

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

Kết quả:

{Hello=2, World=1}

(Ngoài ra còn có khả năng sử dụng groupingByConcurrentđể có hiệu quả cao hơn. Một điều cần lưu ý đối với mã thực của bạn, nếu nó sẽ an toàn trong ngữ cảnh của bạn.)


1
Hoàn hảo! ... từ javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy

6
Sử dụng Function.identity () (với nhập tĩnh) thay vì e -> e làm cho nó dễ đọc hơn một chút: Map <String, Long> Count = list.stream (). Coll (groupingBy (nhận dạng (), đếm () ));
Kuchi

Xin chào, tôi đã tự hỏi liệu ai đó có thể giải thích khía cạnh Bản đồ của mã hay không Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));, chính xác điều gì đang xảy ra vào thời điểm này và bất kỳ liên kết nào có giải thích thêm liên quan đến chủ đề có thể được gửi qua
Trống

@Blank: mà cảm thấy như nó sẽ là tốt nhất như một câu hỏi mới, với bạn giải thích phần nào của nó, bạn làm hiểu đầu tiên. Đi qua mọi khía cạnh của nó (không biết bạn không hiểu về bit nào) sẽ mất một thời gian rất dài - nhiều thời gian hơn tôi sẵn sàng đưa ra một câu trả lời hơn 5 tuổi vào thời điểm này, khi hầu hết là bạn có thể đã hiểu
Jon Skeet

@JonSkeet Cool, tôi sẽ đặt nó vào một câu hỏi mới, mặc dù tôi đã nhấn mạnh khía cạnh mà tôi không hiểu trong câu hỏi của mình. Đó là, toàn bộ đoạn mã tôi đã thêm vào cùng với nó.
Trống

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

8

Dưới đây là ví dụ cho danh sách các Đối tượng

Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));

8

Dưới đây là các tùy chọn hơi khác nhau để hoàn thành nhiệm vụ trong tầm tay.

sử dụng toMap:

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

sử dụng Map::merge:

Map<String, Integer> accumulator = new HashMap<>();
list.forEach(s -> accumulator.merge(s, 1, Math::addExact));

4

Đây là giải pháp đơn giản của StreamEx

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

Giảm mã soạn sẵn: collect(Collectors.


1
Lý do để sử dụng nó trên các luồng Java8 là gì?
Torsten Ojaperv

1

Nếu bạn đang mở để sử dụng thư viện của bên thứ ba, bạn có thể sử dụng Collectors2lớp trong Bộ sưu tập Eclipse để chuyển đổi Listsang Bagsử dụng a Stream. A Baglà một cấu trúc dữ liệu được xây dựng để đếm .

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

Đầu ra:

{World=1, Hello=2}

Trong trường hợp này, bạn có thể chỉ đơn giản là collectsự Listtrực tiếp vào một Bag.

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

Bạn cũng có thể tạo Bagmà không cần sử dụng bằng Streamcách điều chỉnh các Listgiao thức Bộ sưu tập Eclipse.

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

hoặc trong trường hợp cụ thể này:

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

Bạn cũng có thể chỉ cần tạo Túi trực tiếp.

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

A Bag<String>giống như Map<String, Integer>trong đó nó theo dõi nội bộ các phím và số lượng của chúng. Nhưng, nếu bạn hỏi một Mapkhóa mà nó không chứa, nó sẽ trả về null. Nếu bạn hỏi một Bagkhóa mà nó không chứa occurrencesOf, nó sẽ trả về 0.

Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.