J'essaie d'utiliser le code ci-dessous pour calculer la moyenne d'un ensemble de valeurs saisies par un utilisateur et l'afficher dans une jTextArea
, mais cela ne fonctionne pas correctement. Supposons qu'un utilisateur entre 7, 4 et 5, le programme affiche 1 comme moyenne, alors qu'il devrait afficher 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();
}
Quel est le problème avec le code?
Pourquoi utiliser une boucle for maladroite avec un index alors que vous disposez de la boucle for améliorée?
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;
}
Avec Java 8 c'est un peu plus facile :
OptionalDouble average = marks
.stream()
.mapToDouble(a -> a)
.average();
Ainsi, votre valeur moyenne est average.getAsDouble ()
return average.isPresent() ? average.getAsDouble() : 0;
Si vous utilisez Java8 vous pouvez obtenir la moyenne des valeurs d’une liste comme suit:
List<Integer> intList = Arrays.asList(1,2,2,3,1,5);
Double average = intList.stream().mapToInt(val -> val).average().orElse(0.0);
Cela a l'avantage de ne pas avoir de pièces mobiles. Il peut être facilement adapté à une liste d’autres types d’objets en modifiant l’appel de la méthode map.
Par exemple avec les doubles:
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 est requis car il retourne un DoubleStream qui a une méthode average
, contrairement à map
.
ou 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);
}
l'utilisation de orElse(0.0)
supprime les problèmes liés à la non-présence de l'objet optionnel retourné par average
.
Utilisez un double pour la somme, sinon vous faites une division entière et vous n'obtiendrez aucune décimale:
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();
}
ou en utilisant l'API de flux Java 8:
return marks.stream().mapToInt(i -> i).average().orElse(0);
sum += i;
Vous ajoutez l'index; vous devriez ajouter l'élément réel dans la ArrayList
:
sum += marks.get(i);
De plus, pour vous assurer que la valeur de retour n'est pas tronquée, forcez un opérande sur double
et modifiez la signature de votre méthode en double
:
return (double)sum / marks.size();
En utilisant Guava , la syntaxe est simplifiée:
Stats.meanOf(numericList);
List.stream().mapToDouble(a->a).average()
Méthode de calcul correcte et rapide moyenne pour 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();
}
Cette solution prend en compte:
Cela fonctionne conjointement pour List, car toute liste contient moins de 2 ^ 31 int, et il est possible d’utiliser long comme accumulateur.
PS
En fait, pour chaque allocation de mémoire, vous devez utiliser l'ancien style pour le cycle () dans les parties critiques
Vous pouvez utiliser des constructions en boucle standard ou un itérateur/listiterateur pour la même chose:
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 vous utilisez Java 8, vous pouvez utiliser les opérations Stream ou IntSream pour la même chose:
OptionalDouble avg = list.stream().mapToInt(Integer::intValue).average();
System.out.println("Average = " + avg.getAsDouble());
Référence: Calcul de la moyenne d'arrayliste
Voici une version qui utilise BigDecimal
au lieu 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;
}
Lorsque la liste number n'est pas grande, tout semble parfait. Mais si ce n'est pas le cas, une grande prudence est requise pour obtenir exactitude/exactitude .
Prenons double liste à titre d'exemple:
Si la liste double n'est pas assez grande, vous pouvez simplement essayer ceci:
doubles.stream().mapToDouble(d -> d).average().orElse(0.0);
Cependant, si c'est hors de votre contrôle et assez gros, vous devez vous tourner vers BigDecimal comme suit (les méthodes dans les anciennes réponses utilisant BigDecimal sont en fait incorrectes ):
doubles.stream().map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(doubles.size())).doubleValue();
Joignez les tests j’ai réalisé pour démontrer mon propos:
@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 ce qui concerne Integer
ou Long
, vous pouvez également utiliser BigInteger
de la même manière.