计算数组列表的平均值?

Posted

技术标签:

【中文标题】计算数组列表的平均值?【英文标题】:Calculating average of an array list? 【发布时间】:2012-06-03 05:30:05 【问题描述】:

我正在尝试使用以下代码来计算用户输入的一组值的平均值并将其显示在 jTextArea 中,但它无法正常工作。比方说,用户输入 7、4 和 5,程序在应该显示 5.3 时显示 1 作为平均值

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

代码有什么问题?

【问题讨论】:

你不是对分数求和,你是对数组索引i求和。 【参考方案1】:

当您拥有增强的 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;

更新: 正如其他几个人已经指出的那样,在 Java 8 及更高版本中使用 Streams 变得更加简单:

private double calculateAverage(List <Integer> marks) 
    return marks.stream()
                .mapToDouble(d -> d)
                .average()
                .orElse(0.0)

【讨论】:

我会在开始时检查marks.size() == 0,因为如果列表为空,则会除以零 我喜欢 java,但是当你这样做的时候你一定会错过 C# 的 list.Average() 函数:p 简单说明一下,使用笨拙循环的一个原因是它比所谓的文明循环快很多。对于 ArrayLists,for(int i = 0 .... ) 循环比使用迭代器或 for (:) 方法快大约 2 倍,所以即使它更漂亮,它也慢得多!让它变得更快的一个技巧是按如下方式缓存长度:for (int i = 0, len = list.size(); i 它们实际上在正确完成的测试中几乎一样快。有趣的是,“增强的 for 循环”和传统的 for 循环最终的执行速度与 while(i-->0) 一样快,尽管每个循环都有额外的评估/调用。这只是在 se1.7 上运行,arraylist 填充了具有随机 int 作为成员变量的对象并将其计算为总和,以使 vm 执行实际工作。增强循环与使用迭代器迭代自己一样快。如果您使用的是arraylist,则使用增强型毫无意义,因为基于索引的获取更快且导致的gc更少。 这里有点跑题了,但我在 android 中使用了这种方法,但 Android Studio 告诉我 for 循环需要像 for(Object mark: marks) 这样的对象类型(我真的不知道为什么)显然来了循环中出现另一个错误 "Operator '+' cannot be applied to 'java.lang.Double', 'java.lang.Object'" 所以我不得不将 mark 转换为 Double: @ 987654325@【参考方案2】:

Java8 开始,您可以从 List 中获取平均值,如下所示:

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

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

这具有没有移动部件的优点。通过更改 map 方法调用,它可以很容易地适应与其他类型对象的 List 一起使用。

例如双打:

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

注意。 mapToDouble 是必需的,因为它返回具有 average 方法的 DoubleStream,而使用 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 返回的可选对象“不存在”的问题。

【讨论】:

oops - 从未注意到上面的 Java8 答案与我给出的答案相同 在示例2中,为什么dblList包含双精度时需要mapToDouble? @simpleuser - 因为 mapToDouble 返回一个 DoubleStream,它有一个 average 方法。 认为第三种方法有效(使用mapToDouble(BigDecimal::doubleValue).average())。你应该改用BigDecimal::valueOf 实际上即使这样,你还是错了,因为 average 只适用于 primitive types【参考方案3】:

数字不大时,一切似乎都恰到好处。但如果不是,则需要非常谨慎才能实现正确性

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

对于IntegerLong,对应的你可以类似地使用BigInteger

【讨论】:

【参考方案4】:
List.stream().mapToDouble(a->a).average()

【讨论】:

尝试使用代码格式并为您的答案提供一些上下文。以其他答案为例。【参考方案5】:

您可以使用标准循环结构或迭代器/列表迭代器:

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

参考:Calculating average of arraylist

【讨论】:

【参考方案6】:

使用Guava,它在语法上得到简化:

Stats.meanOf(numericList);

【讨论】:

【参考方案7】:

正确快速的方法计算List&lt;Integer&gt;的平均值:

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() 循环

【讨论】:

【参考方案8】:

这里使用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;

【讨论】:

【参考方案9】:

对于 Java 8,它是 a bit easier:

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

因此你的平均值是 average.getAsDouble()

return average.isPresent() ? average.getAsDouble() : 0; 

【讨论】:

average.isPresent() ? average.getAsDouble() : defaultValue 可以进一步简化为optional.orElse( defaultValue ) @OlegEstekhin - 我们不应该使用 mapToInt 而不是 mapToDouble 吗?实际上需要映射吗?【参考方案10】:

对总和使用双精度,否则你正在做一个整数除法,你不会得到任何小数:

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

或使用 Java 8 流 API:

    return marks.stream().mapToInt(i -> i).average().orElse(0);

【讨论】:

在返回之前将种姓转换为双精度会更干净,这样当标记是一个非常大的列表时,您就不会出现任何浮点错误。 关于 Java 8 API 需要哪些导入? @eactor 在上面的例子中不需要额外的导入。【参考方案11】:
sum += i;

您正在添加索引;您应该在ArrayList 中添加实际项目:

sum += marks.get(i);

另外,为确保返回值不会被截断,请将一个操作数强制为double,并将您的方法签名更改为double

return (double)sum / marks.size();

【讨论】:

既然他用的是列表,你应该用sum += marks.get(i);

以上是关于计算数组列表的平均值?的主要内容,如果未能解决你的问题,请参考以下文章

散列表链地址法查找成功的平均查找长度怎么计算

python求一组数组最大值,最小值,平均值

图像平均及其在降噪方面的应用

求开散列表的平均查找长度

根据列表中的两个元素计算列表列表的平均值?

计算元组列表中数值的平均值