Вычисление среднего значения списка массивов?

Я пытаюсь использовать приведенный ниже код для вычисления среднего значения набора значений, которые вводит пользователь, и отображать его в 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();
  }

Что не так с кодом?


person user1419306    schedule 28.05.2012    source источник
comment
Вы не суммируете баллы, вы суммируете индекс массива i.   -  person Tony Ennis    schedule 29.05.2012


Ответы (11)


Зачем использовать неуклюжий цикл for с индексом, если у вас есть усовершенствованный цикл for?

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;
}

Обновление: как уже указывали некоторые другие, это становится намного проще, используя Streams с Java 8 и выше:

private double calculateAverage(List <Integer> marks) {
    return marks.stream()
                .mapToDouble(d -> d)
                .average()
                .orElse(0.0)
}
person Jeshurun    schedule 28.05.2012
comment
Я бы проверил, если mark.size() == 0 в начале, так как это будет делить на ноль, если список пуст - person Axarydax; 07.02.2013
comment
Я люблю java, но вы должны пропустить функцию list.Average() C#, когда вы делаете это: p - person John Humphreys; 24.04.2014
comment
Небольшое замечание: одна из причин использования неуклюжего цикла заключается в том, что он намного быстрее, чем так называемый цивилизованный цикл. Для ArrayLists цикл for(int i = 0 .... ) примерно в 2 раза быстрее, чем использование итератора или подхода for (:), поэтому, хотя он и красивее, он намного медленнее! Один совет, чтобы сделать это еще быстрее, состоит в том, чтобы кэшировать длину следующим образом: for (int i = 0, len = list.size(); i ‹len ; i++). len=list.size() будет выполняться только один раз в начале цикла, и вместо этого каждый раз будет проверяться кэшированное значение len. - person Leo; 08.03.2016
comment
они на самом деле примерно так же быстры в правильно проведенном тесте. интересно, что расширенный цикл for и традиционный цикл for в конечном итоге выполняются так же быстро, как цикл while(i-->0), несмотря на дополнительную оценку/вызов на цикл. это просто работает на se1.7, с arraylist, заполненным объектами, имеющими случайный int в качестве переменной-члена и вычисляющий это в сумме, чтобы заставить vm выполнять реальную работу. расширенный цикл примерно так же быстр, как итерация с помощью итератора. если вы используете arraylist, нет смысла использовать расширенный, поскольку получение на основе индекса выполняется быстрее и меньше вызывает gc. - person Lassi Kinnunen; 01.05.2016
comment
Здесь немного не по теме, но я использовал этот метод в Android, но Android Studio сообщила мне, что для цикла for требуется тип объекта, например for(Object mark: marks) (я действительно не знаю, почему), очевидно, возникает другая ошибка внутри цикла Operator «+» нельзя применить к «java.lang.Double», «java.lang.Object», поэтому мне пришлось привести mark к Double: sum += (Double)mark; - person Cliff Burton; 08.09.2016
comment
Вместо изменения sum на Integer, эффективно упаковывая каждое промежуточное значение, было бы более разумно изменить sum.doubleValue() на (double)sum или просто объявите sum как double в первую очередь. Выполнение суммирования с double по-прежнему будет более эффективным, чем упаковка каждого значения в Integer. - person Holger; 20.11.2019

В Java 8 это немного проще:

OptionalDouble average = marks
            .stream()
            .mapToDouble(a -> a)
            .average();

Таким образом, ваше среднее значение равно medium.getAsDouble()

return average.isPresent() ? average.getAsDouble() : 0; 
person Mariana    schedule 24.06.2015
comment
average.isPresent() ? average.getAsDouble() : defaultValue можно упростить до optional.orElse( defaultValue ) - person Oleg Estekhin; 21.08.2015
comment
@OlegEstekhin - Разве мы не должны использовать mapToInt вместо mapToDouble? На самом деле нужно ли отображение? - person MasterJoe; 25.09.2019

Начиная с Java8 и далее, вы можете получить среднее значение значений из списка следующим образом:

    List<Integer> intList = Arrays.asList(1,2,2,3,1,5);

    Double average = intList.stream().mapToInt(val -> val).average().orElse(0.0);

Преимущество заключается в отсутствии движущихся частей. Его можно легко адаптировать для работы со списком других типов объектов, изменив вызов метода карты.

Например, с двойниками:

    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 как «отсутствующего».

person robjwilkins    schedule 21.08.2015
comment
упс - никогда не замечал ответ Java8 выше, который совпадает с тем, который я дал - person robjwilkins; 21.08.2015
comment
в примере 2, зачем нужен mapToDouble, когда dblList содержит Doubles? - person simpleuser; 15.07.2017
comment
@simpleuser — потому что mapToDouble возвращает DoubleStream, у которого есть метод average. - person robjwilkins; 30.04.2018
comment
Я не думаю, что третий метод работает (с использованием mapToDouble(BigDecimal::doubleValue).average()). Вместо этого вы должны использовать BigDecimal::valueOf. - person Hearen; 26.06.2018
comment
И даже в этом вы все равно ошибаетесь, поскольку среднее работает только для примитивных типов. - person Hearen; 26.06.2018
comment
@Hearen - не уверен, почему ты думаешь, что это не работает? Я обновил пример простым примером junit, который вы можете запустить в своей среде IDE, чтобы убедиться, что код работает. Возможно, вы могли бы опубликовать новый вопрос, если вы не понимаете мой ответ. ваше здоровье. - person robjwilkins; 26.06.2018
comment
@robjwilkins Попробуйте это Arrays.asList(valueOf(Math.pow(10, 308)),valueOf(Math.pow(10, 308)), valueOf(Math.pow(10, 308)),valueOf(Math.pow(10, 308))); - person Hearen; 26.06.2018
comment
Вы можете проверить этот список с помощью моего ответа, который я опубликовал несколько часов назад. - person Hearen; 26.06.2018
comment
Кроме того, я думал, что вы используете List<Double> для обеспечения согласованности, а в вашей последней версии вы используете List<BigDecimal>, который на самом деле предоставит совершенно другой интерфейс, нарушив контракт с пользователями интерфейса. - person Hearen; 26.06.2018
comment
Проблема в вашем решении заключается в том, что If any recorded value is a NaN or the sum is at any point a NaN then the average will be NaN. вы напрямую используете average() для BigDecimal, что фактически делает его пониженным до двойного. - person Hearen; 26.06.2018

Используйте двойную сумму для суммы, иначе вы выполняете целочисленное деление и не получите десятичных знаков:

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);
person Emmanuel Bourg    schedule 28.05.2012
comment
Было бы чище приводить к двойнику непосредственно перед возвратом, чтобы не возникало никаких ошибок с плавающей запятой, когда метки представляют собой очень большой список. - person n00begon; 29.05.2012
comment
что касается API Java 8, какой необходим импорт? - person eactor; 15.09.2015
comment
@eactor В приведенном выше примере дополнительный импорт не требуется. - person Emmanuel Bourg; 17.09.2015

sum += i;

Вы добавляете индекс; вы должны добавить фактический элемент в ArrayList:

sum += marks.get(i);

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

return (double)sum / marks.size();
person Ry-♦    schedule 28.05.2012
comment
Поскольку он использует список, вам следует использовать sum += marks.get(i); - person jahroy; 29.05.2012

Используя Гуава, это синтаксически упрощается:

Stats.meanOf(numericList);
person Sayan Pal    schedule 20.02.2017

Когда число небольшое, все кажется правильным. Но если это не так, для достижения правильности требуется большая осторожность.

Возьмем, к примеру, double:

Если он не большой, как уже упоминалось, вы можете просто попробовать это:

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.

person Hearen    schedule 26.06.2018

Правильный и быстрый способ вычислить среднее значение для 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 выделяет память - вы должны использовать старый стиль цикла for() в критически важных частях

person sibnick    schedule 14.08.2015

Вы можете использовать стандартные циклические конструкции или итератор/список для того же самого:

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());

Ссылка: Вычисление среднего значения массива

person Sekhar Ray    schedule 04.08.2017

Вот версия, которая использует 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;
}
person yglodt    schedule 09.07.2015

person    schedule
comment
Попробуйте использовать форматирование кода и предоставьте некоторый контекст вашему ответу. См. другие ответы в качестве примеров. - person hidralisk; 23.11.2017