Вычисление среднего списка массивов?
Я пытаюсь использовать приведенный ниже код для вычисления среднего значения набора значений, который вводит пользователь, и отображать его в jTextArea
, но он не работает должным образом. Скажем, пользователь вводит 7, 4 и 5, программа отображает 1 как среднее значение, когда она должна отображать 5,3
ArrayList <Integer> marks = new ArrayList();
Collections.addAll(marks, (Integer.parseInt(markInput.getText())));
private void analyzeButtonActionPerformed(java.awt.event.ActionEvent evt) {
analyzeTextArea.setText("Class average:" + calculateAverage(marks));
}
private int calculateAverage(List <Integer> marks) {
int sum = 0;
for (int i=0; i< marks.size(); i++) {
sum += i;
}
return sum / marks.size();
}
Что не так с кодом?
Ответы
Ответ 1
Зачем использовать неуклюжий цикл с индексом, когда у вас есть расширенный цикл?
private double calculateAverage(List <Integer> marks) {
Integer sum = 0;
if(!marks.isEmpty()) {
for (Integer mark : marks) {
sum += mark;
}
return sum.doubleValue() / marks.size();
}
return sum;
}
Ответ 2
С Java 8 это немного проще:
OptionalDouble average = marks
.stream()
.mapToDouble(a -> a)
.average();
Таким образом, ваше среднее значение - average.getAsDouble()
return average.isPresent() ? average.getAsDouble() : 0;
Ответ 3
При использовании Java8 вы можете получить среднее значение из списка следующим образом:
List<Integer> intList = Arrays.asList(1,2,2,3,1,5);
Double average = intList.stream().mapToInt(val -> val).average().orElse(0.0);
Преимущество этого состоит в отсутствии движущихся частей. Его можно легко адаптировать для работы со списком других типов объектов, изменив вызов метода map.
Например, с двойными:
List<Double> dblList = Arrays.asList(1.1,2.1,2.2,3.1,1.5,5.3);
Double average = dblList.stream().mapToDouble(val -> val).average().orElse(0.0);
NB. mapToDouble требуется, потому что оно возвращает DoubleStream, у которого есть метод average
, а использование map
- нет.
или BigDecimals:
@Test
public void bigDecimalListAveragedCorrectly() {
List<BigDecimal> bdList = Arrays.asList(valueOf(1.1),valueOf(2.1),valueOf(2.2),valueOf(3.1),valueOf(1.5),valueOf(5.3));
Double average = bdList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0.0);
assertEquals(2.55, average, 0.000001);
}
использование orElse(0.0)
устраняет проблемы с необязательным объектом, возвращаемым из average
, который "отсутствует".
Ответ 4
Используйте double для суммы, иначе вы делаете целочисленное деление, и вы не получите никаких десятичных знаков:
private double calculateAverage(List <Integer> marks) {
if (marks == null || marks.isEmpty()) {
return 0;
}
double sum = 0;
for (Integer mark : marks) {
sum += mark;
}
return sum / marks.size();
}
или используя API-интерфейс потока Java 8:
return marks.stream().mapToInt(i -> i).average().orElse(0);
Ответ 5
sum += i;
Вы добавляете индекс; вы должны добавить фактический элемент в ArrayList
:
sum += marks.get(i);
Кроме того, чтобы гарантировать, что возвращаемое значение не усечено, заставьте один операнд в double
и измените вашу подпись метода на double
:
return (double)sum / marks.size();
Ответ 6
Используя Guava, он синтаксически упрощается:
Stats.meanOf(numericList);
Ответ 7
Когда число невелико, все кажется правильным. Но если это не так, требуется большая осторожность для достижения правильности.
Возьмем двойной в качестве примера:
Если он не большой, как уже упоминалось, вы можете просто попробовать это:
doubles.stream().mapToDouble(d -> d).average().orElse(0.0);
Однако, если он не под вашим контролем и довольно большой, вы должны обратиться к BigDecimal следующим образом (методы в старых ответах, использующих BigDecimal, на самом деле неверны).
doubles.stream().map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(doubles.size())).doubleValue();
Приложите тесты, которые я провел, чтобы продемонстрировать свою точку зрения:
@Test
public void testAvgDouble() {
assertEquals(5.0, getAvgBasic(Stream.of(2.0, 4.0, 6.0, 8.0)), 1E-5);
List<Double> doubleList = new ArrayList<>(Arrays.asList(Math.pow(10, 308), Math.pow(10, 308), Math.pow(10, 308), Math.pow(10, 308)));
// Double.MAX_VALUE = 1.7976931348623157e+308
BigDecimal doubleSum = BigDecimal.ZERO;
for (Double d : doubleList) {
doubleSum = doubleSum.add(new BigDecimal(d.toString()));
}
out.println(doubleSum.divide(valueOf(doubleList.size())).doubleValue());
out.println(getAvgUsingRealBigDecimal(doubleList.stream()));
out.println(getAvgBasic(doubleList.stream()));
out.println(getAvgUsingFakeBigDecimal(doubleList.stream()));
}
private double getAvgBasic(Stream<Double> doubleStream) {
return doubleStream.mapToDouble(d -> d).average().orElse(0.0);
}
private double getAvgUsingFakeBigDecimal(Stream<Double> doubleStream) {
return doubleStream.map(BigDecimal::valueOf)
.collect(Collectors.averagingDouble(BigDecimal::doubleValue));
}
private double getAvgUsingRealBigDecimal(Stream<Double> doubleStream) {
List<Double> doubles = doubleStream.collect(Collectors.toList());
return doubles.stream().map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(valueOf(doubles.size()), BigDecimal.ROUND_DOWN).doubleValue();
}
Что касается Integer
или Long
, соответственно вы можете использовать BigInteger
аналогичным образом.
Ответ 8
Правильный и быстрый способ вычисления среднего значения для List<Integer>
:
private double calculateAverage(List<Integer> marks) {
long sum = 0;
for (Integer mark : marks) {
sum += mark;
}
return marks.isEmpty()? 0: 1.0*sum/marks.size();
}
Это решение учитывает:
- Переполнение дескриптора
- Не выделяйте память, такую как поток Java8
- Не используйте медленный BigDecimal
Он работает совместно для List, потому что в любом списке содержится меньше 2 ^ 31 int, и можно использовать long в качестве аккумулятора.
PS
Фактически foreach выделяет память - вы должны использовать старый стиль для цикла() в критически важных частях
Ответ 9
Вы можете использовать стандартные конструкции циклов или итератор /listiterator для того же:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
double sum = 0;
Iterator<Integer> iter1 = list.iterator();
while (iter1.hasNext()) {
sum += iter1.next();
}
double average = sum / list.size();
System.out.println("Average = " + average);
Если вы используете Java 8, вы можете использовать операции Stream или IntSream для одного и того же:
OptionalDouble avg = list.stream().mapToInt(Integer::intValue).average();
System.out.println("Average = " + avg.getAsDouble());
Ссылка: Среднее вычисление arraylist
Ответ 10
List.stream().mapToDouble(a->a).average()
Ответ 11
Здесь версия, которая использует BigDecimal
вместо double
:
public static BigDecimal calculateAverage(final List<Integer> values) {
int sum = 0;
if (!values.isEmpty()) {
for (final Integer v : values) {
sum += v;
}
return new BigDecimal(sum).divide(new BigDecimal(values.size()), 2, RoundingMode.HALF_UP);
}
return BigDecimal.ZERO;
}