2014-10-23 2 views
1

Почему IntStream.sum() возвращает значение int? Это какая-то концептуальная ошибка? При создании потока из примерного массива:Обработка исключений IntStream.sum()

int A[] = {2^31-5, 2, 2, 2}; 

Сумма всех элементов превышает максимальное целое значение, но не исключение не генерируется. Кто-нибудь знает, как предотвратить такие ситуации?

+0

Почему бы исключение бросить?Если вам нужно «долго», вам придется [конвертировать в «LongStream»] (http://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#asLongStream- -). –

+0

Использовать LongStream? –

ответ

7

Поведение соответствует тому, что вы получаете при суммировании int s в цикле. Существуют различные способы борьбы с переполнением, все из которых являются компромиссом между общей применимостью и производительностью, и поскольку неясно, какой из них будет использовать/должен использовать API, тот, который широко известен разработчикам Java, был выбран ,

Если вы хотите суммировать, используя больший тип данных, вы можете использовать либо

long sumL=IntStream.of(A).asLongStream().sum();// may still overflow 

или

BigInteger sum = IntStream.of(A).mapToObj(BigInteger::valueOf) 
          .reduce(BigInteger.ZERO, BigInteger::add);// might be slower 

Если вы хотите бросить поведение, вы можете использовать

int sum=IntStream.of(A).reduce(0, (a,b)->{ 
    int c=a+b; 
    if(a>0? b>0 && c<0: b<0&& c>0) 
     throw new ArithmeticException("overflow"); 
    return c; 
}); 

или, что намного проще (интеграция решения Stuart Marks)

int sum=IntStream.of(A).reduce(0, Math::addExact); 
+0

'IntStream.asLongStream' был бы лучшим выбором ... –

+1

@Boris the Spider: Как я мог забыть об этом ... Спасибо – Holger

8

По умолчанию интегральная арифметика в Java не вызывает никаких исключений, если происходит переполнение. Операции, которые переполняются, усекаются до младшего разряда 32 бит для int и 64 бит для long.

Если вы хотите исключение быть выброшены в условиях переполнения, вы можете использовать "точный" семейство функций в java.lang.Math классе:

  • addExact
  • decrementExact
  • incrementExact
  • multiplyExact
  • negateExact
  • subtractExact
  • toIntExact

Они определяются таким же, как обычные арифметические операции на int и long за исключением того, что они бросают ArithmeticException на переполнение вместо усечения результата.

Использование потоков, ваш пример будет выглядеть следующим образом:

int a[] = { Integer.MAX_VALUE - 5, 2, 2, 2 }; 
int sum = Arrays.stream(a).reduce(0, Math::addExact); 

Это выбросит ArithmeticException.

(Обратите внимание, что ваш пример, как написано использует 2^31-5, который является допустимым выражением, как ^ оператор XOR. Результат 24, который, конечно, не даст переполнения.)

+0

Я чувствовал, что есть (должен быть) такой метод, но я искал' java.lang. Integer', поскольку я знал, что он имеет метод 'sum', для альтернативного метода суммы и не думал о' java.lang.Math'. Вот почему я написал ручную проверку как выражение лямбда. Существует ли шаблон решения для определения местоположения класса и метода, который поможет мне найти такие методы в будущем? – Holger

+0

@ Хольгер Да, я знал, что эти функции были добавлены, и первое, что я посмотрел, было 'java.lang.Integer'. Я был немного удивлен, увидев их в 'java.lang.Math'. В определенной степени это вопрос суждения, где такие операции идут. В «Math» уже было много примитивных операций, таких как 'abs()', 'max()', transcendentals, 'round()', 'signum()' и т. Д., Некоторые с перегрузками для разных примитивов, поэтому представляется разумным поставить «точные» операции int 'Math'. –

Смежные вопросы