Calcul de la moyenne d'une liste de tableau?
Je suis en train d'utiliser le code ci-dessous pour calculer la moyenne d'un ensemble de valeurs qu'un utilisateur saisit et l'afficher dans un jTextArea
, mais il ne fonctionne pas correctement. Par exemple, un utilisateur entre 7, 4 et 5, le programme affiche 1 comme moyenne quand il doit 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?
11 réponses
Pourquoi utiliser une boucle for maladroite avec un index lorsque vous avez 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, il est un peu plus facile:
OptionalDouble average = marks
.stream()
.mapToDouble(a -> a)
.average();
Ainsi votre valeur moyenne est moyenne.getAsDouble ()
return average.isPresent() ? average.getAsDouble() : 0;
Si vous utilisez Java8, vous pouvez obtenir la moyenne des valeurs dans 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);
Ceci a l'avantage de ne pas avoir de pièces mobiles. Il peut être facilement adapté pour travailler avec une liste d'autres types d'objets en changeant l'appel de méthode map.
Par exemple avec des 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 renvoie un DoubleStream qui a une méthode average
, alors que l'utilisation de map
ne le fait pas.
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);
}
Utilisation orElse(0.0)
supprime les problèmes avec l'objet optionnel renvoyé du average
étant 'absent'.
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 Java 8 stream:
return marks.stream().mapToInt(i -> i).average().orElse(0);
sum += i;
Vous ajoutez l'index; vous devriez ajouter l'élément réel dans le ArrayList
:
sum += marks.get(i);
Aussi, pour vous assurer que la valeur de retour n'est pas tronquée, forcez un opérande à double
et changez votre signature de méthode à double
:
return (double)sum / marks.size();
Moyen Correct et rapide de calculer la 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:
- gérer le débordement
- n'allouez pas de mémoire comme Java8 stream
- N'utilisez pas lent BigDecimal
Cela fonctionne coorectly pour List, car toute liste contient moins de 2^31 int, et il est possible d'utiliser long comme accumulateur.
PS
En fait, foreach alloue de la mémoire - vous devez utiliser l'ancien style pour le cycle () dans les parties critiques
Vous pouvez utiliser des constructions en boucle standard ou iterator / listiterator 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 des 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 de l'arraylist
Ici 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 listeNombre n'est pas grande, tout semble juste. Mais si ce n'est pas le cas, une grande prudence est requise pour atteindre exactitude/précision.
Prendre double liste 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 grand, vous devez vous tourner vers BigDecimal comme suit (les méthodes dans les anciennes réponses utilisant BigDecimal sont en fait faux):
doubles.stream().map(BigDecimal::valueOf).reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(doubles.size())).doubleValue();
Joindre les tests que j'ai effectués pour démontrer mon point:
@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();
}
Comme pour Integer
ou Long
, Vous pouvez également utiliser BigInteger
de la même manière.