簡體   English   中英

計算數組列表的平均值?

[英]Calculating average of an array list?

我正在嘗試使用以下代碼來計算用戶輸入的一組值的平均值並將其顯示在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();
  }

代碼有什么問題?

使用 Java 8 會更容易一些

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

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

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

當您有增強的 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)
}

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返回的 Optional 對象“不存在”的問題。

對總和使用雙精度值,否則您正在執行整數除法,並且不會得到任何小數:

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);
sum += i;

您正在添加索引; 您應該在ArrayList添加實際項目:

sum += marks.get(i);

此外,為了確保返回值不被截斷,強制一個操作數double並將您的方法簽名更改為double

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

使用Guava ,它在語法上得到了簡化:

Stats.meanOf(numericList);
List.stream().mapToDouble(a->a).average()

數字不大時,一切似乎都恰到好處。 但如果不是,則需要非常小心才能實現正確性

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

正確且快速的方法計算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 作為累加器。

聚苯乙烯

實際上 foreach 分配內存 - 您應該在任務關鍵部分使用舊式 for() 循環

您可以使用標准循環結構或迭代器/列表迭代器來實現:

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的平均值

這是一個使用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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM