计算数组列表的平均值?
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(); ifor(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();
对于Integer
或Long
,对应的你可以类似地使用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<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() 循环
【讨论】:
【参考方案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);
以上是关于计算数组列表的平均值?的主要内容,如果未能解决你的问题,请参考以下文章