Java 스트림과 정수 목록을 합산하는 방법은 무엇입니까?


364

정수 목록을 합치고 싶습니다. 다음과 같이 작동하지만 구문이 옳지 않습니다. 코드를 최적화 할 수 있습니까?

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

5
"하지만 구문이 옳지 않다" 어떻게 생각하십니까? 이것은 일반적인 관용구입니다. mapToLong지도가 가질 수있는 값에 따라 오버플로를 피하기 위해 사용하고 싶을 수도 있습니다.
Alexis C.

3
@JBNizet i -> i개인적으로 매우 분명합니다. 글쎄, 당신은 그 값이 자동으로 언 박싱 될 것이라는 것을 알아야한다. 그러나 그것은 자바 5 이후 사실이다.
Alexis C.

4
@AlexisC. mapToInt ()에 전달되고 경험이 풍부한 개발자이기 때문에 이해할 수 있습니다. 그러나 i-> i는 문맥이 없으면 noop처럼 보입니다. Integer :: intValue는 더 장황하지만 unboxing 작업을 명시 적으로 만듭니다.
JB 니 제트

1
@JBNizet 메소드를 호출하는 사람들은 메소드 를 호출 할 때마다 foo(int i)쓰지 않습니다 foo(myInteger.intValue());(적어도 나는 기대하지 않습니다 !!). Integer::intValue더 명시 적이지만 귀하도 여기에 동일하게 적용됩니다. 사람들은 한 번만 배우면 끝납니다 :-). 마술 난독 화와는 다릅니다.
Alexis C.

4
@JB Nizet : 글쎄요, i -> ino-op처럼 보이고 개념적 으로 는 no-op입니다. 물론 후드 아래에서 Integer.intValue()호출되지만 후드 아래에서 더 깊어지면 해당 메소드가 소스 코드에서와 같이 정확하게 no-op가되도록 인라인됩니다. Integer::intValue바이트 코드에서 합성 메소드를 작성하지 않는 보너스 포인트가 있지만 소스 코드를 구성하는 방법을 결정 해야하는 것은 아닙니다.
Holger

답변:


498

이것은 작동하지만, i -> i자동 언 박싱을 수행하고 있기 때문에 이상한 느낌이 든다. 다음 중 하나가 작동하고 원래 구문으로 컴파일러가 수행하는 작업을 더 잘 설명합니다.

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

두 가지 옵션을 더 제안합니다.

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 ()와 같은 여러 특수 축소 형식뿐만 아니라 reduce () 및 collect ()라는 여러 일반 축소 연산 형식이 있습니다.

물론 이러한 작업은 다음과 같이 간단한 순차 루프로 쉽게 구현할 수 있습니다.

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가 가진 것이 훨씬 좋고 명확합니다. 이 코드에는 언 박싱 및 복싱 작업이 필요합니다.
JB 니 제트

1
@JBNizet 탈출 분석이 권투를 제거하지 않는 한. 당신이 할 수 있는지 확인하기 위해 시도해야 할 것입니다.
피터로 레이

6
(x, y)-> x + y는 x와 y를 개봉하여 합계 한 다음 결과를 상자에 넣어야합니다. 그리고 스트림의 다음 요소로 결과를 추가하기 위해 다시 시작하십시오.
JB 니 제트

3
Integer :: sum도 같은 문제를 겪습니다. 그리고 mapToInt ()를 사용하여 IntStream을 가지면 sum ()을 호출하는 것이 reduce ()를 호출하는 것보다 더 간단합니다.
JB 니 제트

3
docs.oracle.com/javase/8/docs/api/java/lang/…을 참조하십시오 . Integer.sum ()의 두 인수는 int 유형입니다. 따라서 스트림의 두 정수는 상자를 해제하여 메소드에 인수로 전달해야합니다. 이 메소드는 int를 리턴하지만 reduce ()는 BinaryOperator <Integer>를 인수로 사용하므로 Integer를 리턴합니다. 따라서 합계의 결과는 정수로 상자에 넣어 져야합니다.
JB 니 제트

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
Alex Salauyou

1
당신은 오래 반환, 그래서 Long::sum보다 낫 습니다 Integer::sum.
Andrei Damian-Fekete

16

reduce()정수 목록을 합산하는 데 사용할 수 있습니다 .

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

11

collect 메소드를 사용하여 정수 목록을 추가 할 수 있습니다.

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

6

이것은 inttype array ( longarray LongStream, doublearray DoubleStream등) 를 요약하는 가장 짧은 방법 입니다. 모든 원시 정수 또는 부동 소수점 유형이 Stream구현을 갖는 것은 아닙니다.

IntStream.of(integers).sum();

불행히도 우리는 int-array가 없습니다. 그래서 IntStream.of()우리를 제외하고있는 거 만드는 뭔가 유령 같은,이 문제에 대해 작동하지 않습니다 :IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
카플란

필요하지 않습니다 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);

고맙습니다 그것은 내 scenerio 중 하나에서 나를 도와주었습니다
A_01

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

대부분의 측면이 다룹니다. 그러나 Integer, Long과는 별도로 다른 데이터 유형의 집계를 찾아야 할 수도 있습니다 (특별한 스트림 지원이 이미 존재 함). 예를 들어 BigInteger를 사용한 stram과 같은 유형의 경우 다음과 같이 reduce 작업을 사용할 수 있습니다

list.stream (). reduce ((bigInteger1, bigInteger2)-> bigInteger1.add (bigInteger2))

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.