Làm cách nào để tổng hợp danh sách các số nguyên với các luồng java?


364

Tôi muốn tổng hợp một danh sách các số nguyên. Nó hoạt động như sau, nhưng cú pháp không cảm thấy đúng. Mã có thể được tối ưu hóa?

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

5
"Nhưng cú pháp không cảm thấy đúng" Điều gì khiến bạn nghĩ như vậy? Đây là thành ngữ thông thường. Có thể bạn muốn sử dụng mapToLongđể tránh tràn, tùy thuộc vào các giá trị mà bản đồ của bạn có thể có.
Alexis C.

3
@JBNizet Tôi thấy i -> irất rõ ràng, cá nhân. Chà, vâng, bạn cần biết rằng giá trị sẽ được tự động mở hộp, nhưng đó là sự thật kể từ Java 5 ...
Alexis C.

4
@AlexisC. có thể hiểu được vì nó được chuyển cho mapToInt () và vì tôi là nhà phát triển có kinh nghiệm. Nhưng tôi -> i, không có ngữ cảnh, trông giống như một noop. Integer :: intValue dài dòng hơn, nhưng làm cho hoạt động unboxing rõ ràng.
JB Nizet

1
@JBNizet Những người gọi phương thức foo(int i)này không viết foo(myInteger.intValue());mỗi lần họ gọi nó (hoặc ít nhất là tôi không mong đợi !!). Tôi đồng ý với bạn rằng Integer::intValuerõ ràng hơn nhưng tôi nghĩ điều tương tự áp dụng ở đây. Mọi người chỉ nên học nó một lần và sau đó bạn đã hoàn thành :-). Nó không giống như nếu đó là một phép thuật xáo trộn.
Alexis C.

4
@JB Nizet: tốt, i -> itrông giống như một no-op và theo quan niệm, nó một no-op. Chắc chắn, dưới mui xe Integer.intValue()được gọi, nhưng thậm chí sâu hơn dưới mui xe, các phương thức đó được đưa vào để trở thành chính xác không có gì giống như trong mã nguồn. Integer::intValuecó điểm thưởng là không tạo ra một phương thức tổng hợp trong mã byte nhưng nó không phải là yếu tố quyết định cách tổ chức mã nguồn của bạn.
Holger

Câu trả lời:


498

Điều này sẽ hoạt động, nhưng i -> iđang thực hiện một số unboxing tự động, đó là lý do tại sao nó "cảm thấy" lạ. Một trong những điều sau đây sẽ hoạt động và giải thích rõ hơn những gì trình biên dịch đang làm dưới mui xe với cú pháp ban đầu của bạn:

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

2
Nếu chúng ta có BigInteger :) thì sao?
GOXR3PLUS

13
Một lựa chọn đơn giản làBigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matthew

158

Tôi đề nghị thêm 2 lựa chọn:

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

Cái thứ hai sử dụng Collectors.summingInt()collector, cũng có một summingLong()collector mà bạn sẽ sử dụng mapToLong.


Và một tùy chọn thứ ba: Java 8 giới thiệu một bộ LongAddertích lũy rất hiệu quả được thiết kế để tăng tốc tổng hợp trong các luồng song song và môi trường đa luồng. Đây là một ví dụ sử dụng:

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

86

Từ các tài liệu

Các hoạt động rút gọn Hoạt động rút gọn (còn gọi là nếp gấp) lấy một chuỗi các phần tử đầu vào và kết hợp chúng thành một kết quả tóm tắt duy nhất bằng cách áp dụng lặp lại một hoạt động kết hợp, chẳng hạn như tìm tổng hoặc tối đa của một tập hợp số hoặc tích lũy các phần tử vào một danh sách. Các lớp luồng có nhiều dạng hoạt động giảm chung, được gọi là less () và coll (), cũng như nhiều dạng rút gọn chuyên biệt như sum (), max () hoặc Count ().

Tất nhiên, các hoạt động như vậy có thể được thực hiện dễ dàng như các vòng lặp tuần tự đơn giản, như trong:

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

Tuy nhiên, có những lý do chính đáng để thích một hoạt động giảm hơn tích lũy đột biến như ở trên. Không chỉ là giảm "trừu tượng" hơn - nó hoạt động trên toàn bộ luồng chứ không phải là các phần tử riêng lẻ - nhưng hoạt động giảm được xây dựng đúng cách vốn đã song song, miễn là (các) hàm được sử dụng để xử lý các phần tử là liên kết và không quốc tịch Ví dụ: được cung cấp một dòng số mà chúng tôi muốn tìm tổng, chúng tôi có thể viết:

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

hoặc là:

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

Các hoạt động giảm này có thể chạy an toàn song song với hầu như không sửa đổi:

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

Vì vậy, đối với bản đồ bạn sẽ sử dụng:

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

Hoặc là:

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

2
Những gì OP có là tốt hơn nhiều, và cũng rõ ràng hơn. Mã này sẽ liên quan đến toàn bộ các hoạt động unboxing và đấm bốc.
JB Nizet

1
@JBNizet Trừ khi phân tích thoát hiểm loại bỏ quyền anh. Bạn sẽ phải thử nó để xem nếu nó có thể.
Peter Lawrey

6
(x, y) -> x + y cần bỏ hộp x và y, tính tổng chúng và sau đó đóng hộp kết quả. Và bắt đầu lại để thêm kết quả với phần tử tiếp theo của luồng và lặp lại.
JB Nizet

3
Integer :: sum chịu cùng một vấn đề. Và nếu bạn sử dụng mapToInt () để có IntStream, việc gọi sum () trên đó đơn giản hơn so với gọi hàm less ().
JB Nizet

3
Xem docs.oracle.com/javase/8/docs/api/java/lang/ . Hai đối số của Integer.sum () có kiểu int. Vì vậy, hai số nguyên từ luồng phải được bỏ hộp để được truyền dưới dạng đối số cho phương thức. Phương thức trả về một int, nhưng less () lấy BinaryOperator <Integer> làm đối số, do đó trả về một Integer. Vì vậy, kết quả của tổng phải được đóng hộp thành Integer.
JB Nizet

28

Bạn có thể sử dụng phương thức rút gọn:

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

hoặc là

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

9
Đã có tích lũy như vậy cho int, đó làInteger::sum
Alex Salauyou

1
Bạn sẽ trở lại lâu, vì vậy nó sẽ tốt Long::sumhơn Integer::sum.
Andrei Damian-Fekete

16

Bạn có thể sử dụng reduce()để tổng hợp một danh sách các số nguyên.

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

11

Bạn có thể sử dụng phương pháp thu thập để thêm danh sách các số nguyên.

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

6

Đây sẽ là cách ngắn nhất để tổng hợp intmảng loại (cho longmảng LongStream, cho doublemảng DoubleStreamvà vv). Không phải tất cả các kiểu số nguyên hoặc dấu phẩy động đều có cách Streamthực hiện.

IntStream.of(integers).sum();

Thật không may, chúng tôi không có bất kỳ mảng int. Vì vậy, IntStream.of()sẽ không có tác dụng cho vấn đề này, trừ khi chúng ta tạo ra thứ gì đó ma quái như thế này:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan

Không cần, điều này là đủ integers.values().stream().mapToInt( Integer::intValue ).sum().
Sachith Dickwella

3

Có thể điều này sẽ giúp những người có đối tượng trong danh sách.

Nếu bạn có một danh sách các đối tượng và muốn tổng hợp các trường cụ thể của đối tượng này, hãy sử dụng bên dưới.

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

Cảm ơn nó đã giúp tôi trong một trong những scenerio của tôi
A_01

1

Tôi đã tuyên bố một danh sách các số nguyên.

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

Bạn có thể thử sử dụng những cách khác nhau dưới đây.

Sử dụng mapToInt

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

Sử dụng summarizingInt

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

Sử dụng 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

Hầu hết các khía cạnh được bảo hiểm. Nhưng có thể có một yêu cầu để tìm tổng hợp các loại dữ liệu khác ngoài Integer, Long (đã có hỗ trợ luồng chuyên biệt). Ví dụ: stram với BigInteger Đối với loại như vậy, chúng ta có thể sử dụng giảm hoạt động như

list.stream (). giảm ((bigInteger1, bigInteger2) -> bigInteger1.add (bigInteger2))

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.