Calculando el promedio de una lista de matrices?


Estoy tratando de usar el siguiente código para calcular el promedio de un conjunto de valores que ingresa un usuario y mostrarlo en un jTextArea pero no funciona correctamente. Digamos que un usuario ingresa 7, 4 y 5, el programa muestra 1 como el promedio cuando debería mostrar 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();
  }

¿Qué tiene de malo el código?

Author: giannis christofakis, 2012-05-29

11 answers

¿Por qué usar un bucle for torpe con un índice cuando tiene el bucle for mejorado?

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;
}
 59
Author: Jeshurun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-21 20:34:37

Con Java 8 es un poco más fácil :

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

Por lo tanto, su valor promedio es promedio.getAsDouble ()

return average.isPresent() ? average.getAsDouble() : 0; 
 50
Author: Mariana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-24 08:46:32

Si usa Java8 puede obtener el promedio de los valores de una Lista de la siguiente manera:

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

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

Esto tiene la ventaja de no tener partes móviles. Se puede adaptar fácilmente para trabajar con una Lista de otros tipos de objetos cambiando la llamada al método map.

Por ejemplo con Dobles:

    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 es necesario porque devuelve un DoubleStream que tiene un método average, mientras que usar map no lo hace.

O bigdecimales:

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

Utilizando orElse(0.0) elimina los problemas con el objeto opcional devuelto desde el average siendo 'no presente'.

 19
Author: robjwilkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-26 09:59:58

Use un doble para la suma, de lo contrario está haciendo una división entera y no obtendrá decimales:

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

O usando la API de flujo Java 8:

    return marks.stream().mapToInt(i -> i).average().orElse(0);
 12
Author: Emmanuel Bourg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-28 09:27:51
sum += i;

Estás agregando el índice; deberías agregar el elemento real en el ArrayList:

sum += marks.get(i);

También, para asegurarse de que el valor devuelto no esté truncado, fuerce un operando a double y cambie su firma de método a double:

return (double)sum / marks.size();
 10
Author: Ry-,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-28 23:50:40

Usando Guayaba , se simplifica sintácticamente:

Stats.meanOf(numericList);
 2
Author: Sayan Pal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-20 15:39:04
List.stream().mapToDouble(a->a).average()
 2
Author: Dinesh Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-26 05:24:26

La forma correcta y rápida de calcular el promedio para 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();
}

Esta solución tiene en cuenta:

  • Manejar el desbordamiento
  • No asigne memoria como Java8 stream
  • No use BigDecimal lento

Funciona de forma coordinada para la Lista, porque cualquier lista contiene menos de 2^31 int, y es posible usar long como acumulador.

PS

En realidad foreach asignar memoria-se debe utilizar el estilo antiguo para el ciclo() en las partes de misión crítica

 1
Author: sibnick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-14 04:59:06

Puede usar construcciones de bucle estándar o iterador / listiterador para el mismo:

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

Si usa Java 8, puede usar operaciones Stream o IntSream para lo mismo:

OptionalDouble avg = list.stream().mapToInt(Integer::intValue).average();
System.out.println("Average = " + avg.getAsDouble());

Referencia: Calculando el promedio de arraylist

 1
Author: Sekhar Ray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-04 20:05:24

Aquí una versión que usa BigDecimal en lugar de 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;
}
 0
Author: yglodt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-09 09:37:21

Cuando la lista de números no es grande, todo parece correcto. Pero si no lo es, se requiere gran precaución para lograr corrección/precisión.

Tome lista doble como ejemplo:

Si la lista double no es del todo grande, puedes probar esto:

doubles.stream().mapToDouble(d -> d).average().orElse(0.0);

Sin embargo, si está fuera de su control y es bastante grande, debe recurrir a BigDecimal de la siguiente manera (los métodos en las respuestas antiguas que usan BigDecimal en realidad son incorrecto):

doubles.stream().map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add)
       .divide(BigDecimal.valueOf(doubles.size())).doubleValue();

Adjunte las pruebas Que realicé para demostrar mi punto:

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

En cuanto a Integer o Long, correspondientemente se puede usar BigInteger de manera similar.

 0
Author: Hearen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-26 11:58:59