วิธีการรวมรายการจำนวนเต็มกับจาวากระแส?


364

ฉันต้องการรวมรายการจำนวนเต็ม มันใช้งานได้ดังต่อไปนี้ แต่ไวยากรณ์ไม่ถูกต้อง รหัสสามารถปรับให้เหมาะสมหรือไม่

Map<String, Integer> integers;
integers.values().stream().mapToInt(i -> i).sum();

5
"แต่ไวยากรณ์ไม่รู้สึกถูกต้อง"อะไรทำให้คุณคิดอย่างนั้น นี่เป็นสำนวนปกติ บางทีคุณอาจต้องการใช้mapToLongเพื่อหลีกเลี่ยงการล้นเกินขึ้นอยู่กับค่าแผนที่ของคุณ
Alexis C.

3
@JBNizet ฉันพบว่าi -> iชัดเจนมากส่วนตัว ใช่คุณจำเป็นต้องรู้ว่าค่าจะถูกยกเลิกโดยอัตโนมัติ แต่มันเป็นเรื่องจริงตั้งแต่ Java 5 ...
อเล็กซิสซี

4
@AlexisC มันเข้าใจได้เพราะมันถูกส่งผ่านไปยัง mapToInt () และเพราะฉันเป็นนักพัฒนาที่มีประสบการณ์ แต่ฉัน -> i โดยไม่มีบริบทดูเหมือนว่า noop จำนวนเต็ม :: intValue เป็น verbose มากขึ้น แต่ทำให้การดำเนินการ unboxing ชัดเจน
JB Nizet

1
@JBNizet ผู้ที่เรียกใช้เมธอดfoo(int i)อย่าเขียนfoo(myInteger.intValue());ทุกครั้งที่เรียกว่า (หรืออย่างน้อยฉันก็คาดหวังว่าจะไม่ !!) ฉันเห็นด้วยกับคุณที่Integer::intValueชัดเจนกว่า แต่ฉันคิดว่าเหมือนกันกับที่นี่ ผู้คนควรเรียนรู้เพียงครั้งเดียวจากนั้นคุณก็ทำเสร็จ :-) มันไม่เหมือนกับว่ามันเป็นการทำให้งงงวยมายากล
Alexis C.

4
@JB Nizet: ดีi -> iดูเหมือนว่าไม่มี op และความคิดมันเป็น no-op แน่นอนว่าภายใต้ประทุนInteger.intValue()ได้รับการเรียกใช้ แต่ยิ่งลึกลงไปใต้ฝากระโปรงวิธีการดังกล่าวจะกลายเป็นแบบไม่มีการใช้งานเหมือนที่ปรากฏในซอร์สโค้ด Integer::intValueมีจุดโบนัสที่ไม่ได้สร้างวิธีการสังเคราะห์ในรหัสไบต์ แต่ไม่ใช่สิ่งที่ควรผลักดันการตัดสินใจของคุณในการจัดระเบียบซอร์สโค้ดของคุณ
Holger

คำตอบ:


498

สิ่งนี้จะได้ผล แต่สิ่งที่i -> iกำลังทำอยู่คือการทำ unboxing อัตโนมัติซึ่งเป็นสาเหตุที่ทำให้ "รู้สึก" แปลก ทั้งสองข้อต่อไปนี้จะทำงานได้ดีขึ้นและอธิบายสิ่งที่คอมไพเลอร์ทำงานภายใต้ประทุนด้วยไวยากรณ์ดั้งเดิมของคุณ:

integers.values().stream().mapToInt(i -> i.intValue()).sum();
integers.values().stream().mapToInt(Integer::intValue).sum();

2
จะเป็นอย่างไรถ้าเรามี BigInteger :)
GOXR3PLUS

13
ทางเลือกง่าย ๆ อย่างหนึ่งคือBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matthew

158

ฉันขอแนะนำอีก 2 ตัวเลือก:

integers.values().stream().mapToInt(Integer::intValue).sum();
integers.values().stream().collect(Collectors.summingInt(Integer::intValue));

คนที่สองใช้Collectors.summingInt()เก็บนอกจากนี้ยังมีของสะสมที่คุณจะใช้กับsummingLong()mapToLong


และตัวเลือกที่สาม: Java 8 มีประสิทธิภาพมาก LongAdderสะสมที่ซึ่งออกแบบมาเพื่อการสรุปที่รวดเร็วขึ้นในสตรีมแบบขนานและสภาพแวดล้อมแบบมัลติเธรด นี่คือตัวอย่างการใช้งาน:

LongAdder a = new LongAdder();
map.values().parallelStream().forEach(a::add);
sum = a.intValue();

86

จากเอกสาร

การดำเนินการลดการดำเนินการลด (เรียกอีกอย่างว่าพับ) ใช้ลำดับขององค์ประกอบการป้อนข้อมูลและรวมไว้ในผลสรุปเดียวโดยการประยุกต์ใช้การดำเนินการรวมซ้ำเช่นการหาผลรวมหรือสูงสุดของชุดของตัวเลขหรือองค์ประกอบที่สะสมลงใน รายการ คลาสสตรีมมีรูปแบบการลดทั่วไปหลายรูปแบบเรียกว่าลด () และรวบรวม () รวมถึงรูปแบบการลดพิเศษหลายรูปแบบเช่น sum (), max (), หรือ count ()

แน่นอนว่าการดำเนินการดังกล่าวสามารถนำมาใช้อย่างง่ายดายเป็นลูปแบบต่อเนื่องแบบง่ายๆเช่นเดียวกับใน:

int sum = 0;
for (int x : numbers) {
   sum += x;
}

อย่างไรก็ตามมีเหตุผลที่ดีที่ต้องการลดการดำเนินการมากกว่าการสะสมการกลายพันธุ์เช่นข้างต้น ไม่เพียง แต่เป็นการลด "ความเป็นนามธรรมมากขึ้น" - มันทำงานบนกระแสข้อมูลโดยรวมมากกว่าองค์ประกอบส่วนบุคคล - แต่การลดขนาดที่สร้างขึ้นอย่างเหมาะสมนั้นขนานกันโดยเนื้อแท้ตราบใดที่ฟังก์ชันที่ใช้ในการประมวลผลองค์ประกอบนั้นเชื่อมโยงกัน และไร้สัญชาติ ตัวอย่างเช่นหากมีตัวเลขที่เราต้องการหาผลรวมเราสามารถเขียน:

int sum = numbers.stream().reduce(0, (x,y) -> x+y);

หรือ:

int sum = numbers.stream().reduce(0, Integer::sum);

การดำเนินการลดเหล่านี้สามารถทำงานได้อย่างปลอดภัยพร้อมกันโดยแทบไม่มีการดัดแปลงใด ๆ

int sum = numbers.parallelStream().reduce(0, Integer::sum);

ดังนั้นสำหรับแผนที่คุณจะใช้:

integers.values().stream().mapToInt(i -> i).reduce(0, (x,y) -> x+y);

หรือ:

integers.values().stream().reduce(0, Integer::sum);

2
สิ่งที่ OP มีนั้นดีกว่าและชัดเจนกว่า รหัสนี้จะเกี่ยวข้องกับการดำเนินการ unboxing และ Boxing ทั้งหมด
JB Nizet

1
@JBNizet เว้นเสียแต่ว่าการวิเคราะห์ escape จะกำจัดการชกมวย คุณจะต้องลองดูว่าทำได้หรือไม่
Peter Lawrey

6
(x, y) -> x + y ต้องยกเลิกการทำ x และ y, รวมพวกมันแล้วทำเครื่องหมายผลลัพธ์ และเริ่มต้นอีกครั้งเพื่อเพิ่มผลลัพธ์ด้วยองค์ประกอบถัดไปของสตรีมและอีกครั้งและอีกครั้ง
JB Nizet

3
จำนวนเต็ม :: ผลรวมประสบปัญหาเดียวกัน และถ้าคุณใช้ mapToInt () เพื่อใช้งาน IntStream การโทรหา Sum () จะตรงไปตรงมามากกว่าการโทรลด ()
JB Nizet

3
ดูdocs.oracle.com/javase/8/docs/api/java/lang/... อาร์กิวเมนต์สองตัวของ Integer.sum () เป็นประเภท int ดังนั้นจำนวนเต็มทั้งสองจากสตรีมจะต้องไม่ถูกทำกล่องเพื่อส่งผ่านเป็นอาร์กิวเมนต์ไปยังเมธอด วิธีการส่งกลับ int แต่ลด () ใช้ BinaryOperator <Integer> เป็นอาร์กิวเมนต์ซึ่งจะส่งกลับจำนวนเต็ม ดังนั้นผลลัพธ์ของการรวมจะต้องถูกบรรจุลงใน Integer
JB Nizet

28

คุณสามารถใช้วิธีการลด:

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, (x, y) -> x + y);

หรือ

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, Integer::sum);

9
มีอยู่แล้วสะสมดังกล่าวintก็เป็นInteger::sum
อเล็กซ์ Salauyou

1
คุณกำลังจะกลับมาเป็นเวลานานดังนั้นมันจะดีกว่ากว่าLong::sum Integer::sum
Andrei Damian-Fekete


11

คุณสามารถใช้วิธีการรวบรวมเพื่อเพิ่มรายการจำนวนเต็ม

List<Integer> list = Arrays.asList(2, 4, 5, 6);
int sum = list.stream().collect(Collectors.summingInt(Integer::intValue));

6

นี่จะเป็นวิธีที่สั้นที่สุดในการสรุปintประเภทอาร์เรย์ (สำหรับlongอาร์เรย์LongStreamสำหรับdoubleอาร์เรย์DoubleStreamและอื่น ๆ ) ไม่ใช่จำนวนเต็มดั้งเดิมหรือชนิดจำนวนจุดลอยตัวทั้งหมดที่มีการStreamใช้งาน

IntStream.of(integers).sum();

น่าเสียดายที่เราไม่มี int-array ดังนั้นIntStream.of()จะไม่ทำงานสำหรับปัญหานี้เว้นแต่ว่าเราจะทำอะไรที่น่ากลัวเช่นนี้:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan

ไม่ต้องการนี่ก็เพียงพอแล้ว integers.values().stream().mapToInt( Integer::intValue ).sum()แล้ว
Sachith Dickwella

3

ขอให้สิ่งนี้ช่วยผู้ที่มีสิ่งของในรายการ

หากคุณมีรายการของวัตถุและต้องการที่จะรวมเขตข้อมูลที่เฉพาะเจาะจงของวัตถุนี้ใช้ด้านล่าง

List<ResultSom> somList = MyUtil.getResultSom();
BigDecimal result= somList.stream().map(ResultSom::getNetto).reduce(
                                             BigDecimal.ZERO, BigDecimal::add);

ขอบคุณมันช่วยฉันในหนึ่งใน
ฉาก

1

ฉันได้ประกาศรายชื่อจำนวนเต็ม

ArrayList<Integer> numberList = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));

คุณสามารถลองใช้วิธีต่างๆด้านล่างนี้

การใช้ mapToInt

int sum = numberList.stream().mapToInt(Integer::intValue).sum();

การใช้ summarizingInt

int sum = numberList.stream().collect(Collectors.summarizingInt(Integer::intValue)).getSum();

การใช้ reduce

int sum = numberList.stream().reduce(Integer::sum).get().intValue();

-1
class Pojo{
    int num;

    public Pojo(int num) {
        super();
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

List<Pojo> list = new ArrayList<Pojo>();
            list.add(new Pojo(1));
            list.add(new Pojo(5));
            list.add(new Pojo(3));
            list.add(new Pojo(4));
            list.add(new Pojo(5));

            int totalSum = list.stream().mapToInt(pojo -> pojo.getNum()).sum();
            System.out.println(totalSum);

-1

ส่วนใหญ่ครอบคลุมด้าน แต่อาจมีข้อกำหนดในการค้นหาการรวมประเภทข้อมูลอื่นนอกเหนือจากจำนวนเต็ม, แบบยาว (ซึ่งมีการสนับสนุนกระแสข้อมูลเฉพาะอยู่แล้ว) สำหรับตัวอย่างเช่น stram กับ BigInteger สำหรับประเภทนี้เราสามารถใช้ลดการทำงานเช่น

รายการ. สตรีม (). ลด ((bigInteger1, bigInteger2) -> bigInteger1.add (bigInteger2))

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