จัดกลุ่มตามจำนวนใน Java 8 stream API


170

ฉันพยายามหาวิธีง่ายๆใน Java 8 stream API เพื่อทำการจัดกลุ่มฉันออกมาด้วยวิธีที่ซับซ้อนนี้!

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

ฉันขอขอบคุณที่คุณป้อนข้อมูล


1
คุณพยายามทำอะไรที่นี่
Keppil

2
เป็นกรณีที่พบบ่อยมากตัวอย่างเช่นฉันมีข้อผิดพลาดเกิดขึ้นในช่วงเวลาหนึ่งและฉันต้องการดูสถิติเกี่ยวกับจำนวนของการเกิดขึ้นต่อวันในแต่ละช่วงเวลา
Muhammad Hewedy

คำตอบ:


340

ฉันคิดว่าคุณกำลังมองหาการโอเวอร์โหลดที่ใช้เวลาอีกอันCollectorเพื่อระบุว่าจะทำอย่างไรกับแต่ละกลุ่ม ... แล้วCollectors.counting()ทำการนับ:

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

ผลลัพธ์:

{Hello=2, World=1}

(นอกจากนี้ยังมีความเป็นไปได้ในการใช้groupingByConcurrentงานอย่างมีประสิทธิภาพมากขึ้นสิ่งที่ควรคำนึงถึงสำหรับรหัสจริงของคุณหากมันปลอดภัยในบริบทของคุณ)


1
ที่สมบูรณ์แบบ! ... จาก javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy

6
การใช้ Function.identity () (พร้อมการนำเข้าแบบคงที่) แทน e -> e ทำให้การอ่านดีขึ้นเล็กน้อย: แผนที่ <String, Long> counted = list.stream (). collect (groupingBy (identity (), count () ));
Kuchi

สวัสดีฉันสงสัยว่าใครบางคนสามารถอธิบายมุมมองแผนที่ของรหัสMap<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));ว่าเกิดอะไรขึ้น ณ จุดนี้และลิงก์ใด ๆ ที่มีคำอธิบายเพิ่มเติมเกี่ยวกับหัวข้อที่สามารถส่งไปได้
Blank

@Blank: ที่รู้สึกเหมือนมันจะดีที่สุดเป็นคำถามใหม่กับคุณอธิบายว่าส่วนใดของมันคุณไม่เข้าใจก่อน ผ่านทุกแง่มุมของมัน (ไม่รู้ว่าคุณไม่เข้าใจบิตใด) จะใช้เวลานานมาก - มีเวลามากกว่าที่ฉันยินดีที่จะตอบคำถามที่อายุเกิน 5 ปี ณ จุดนี้เมื่อคุณส่วนใหญ่ อาจเข้าใจแล้ว
Jon Skeet

@ JonSkeet เจ๋งฉันจะใส่คำถามใหม่ถึงแม้ว่าฉันจะเน้นในแง่มุมที่ฉันไม่เข้าใจในคำถามของฉัน นั่นคือข้อมูลโค้ดทั้งหมดที่ฉันเพิ่มเข้าไปด้วย
ว่าง

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

นี่คือตัวอย่างสำหรับรายการของวัตถุ

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

8

นี่คือตัวเลือกที่แตกต่างกันเล็กน้อยในการทำงานให้สำเร็จ

ใช้toMap:

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

ใช้Map::merge:

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

4

นี่คือทางออกที่ง่ายโดยStreamEx

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

ลดรหัสสำเร็จรูป: collect(Collectors.


1
อะไรคือเหตุผลที่จะใช้งานผ่านสตรีม Java8
Torsten Ojaperv

1

หากคุณเปิดให้ใช้ห้องสมุดของบุคคลที่สามคุณสามารถใช้Collectors2เรียนในEclipse คอลเลกชันการแปลงListไปใช้Bag เป็นโครงสร้างข้อมูลที่สร้างขึ้นสำหรับการนับStreamBag

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

เอาท์พุท:

{World=1, Hello=2}

ในกรณีนี้โดยเฉพาะอย่างยิ่งที่คุณสามารถเพียงโดยตรงในcollectListBag

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

คุณยังสามารถสร้างBagโดยไม่ต้องใช้ a StreamโดยปรับListด้วยโปรโตคอล Eclipse Collections

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

หรือในกรณีนี้:

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

คุณสามารถสร้างกระเป๋าได้โดยตรง

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

A Bag<String>เป็นเหมือนการMap<String, Integer>ที่ภายในติดตามคีย์และจำนวนของพวกเขา แต่ถ้าคุณถามสำหรับคีย์มันไม่ได้มีก็จะกลับมาMap nullหากคุณขอBagรหัสที่ไม่มีการใช้occurrencesOfงานมันจะส่งคืนค่า 0

หมายเหตุ:ฉันเป็นคอมมิชชันสำหรับ Eclipse Collections

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.