web-dev-qa-db-fra.com

Est-ce que mapToDouble () est vraiment nécessaire pour additionner une liste <Double> avec Java 8 flux?

Autant que je sache, le moyen de sommer un List<Double> En utilisant Java 8 flux est le suivant:

List<Double> vals = . . . ;
double sum = vals.stream().mapToDouble(Double::doubleValue).sum();

Pour moi, la mapToDouble(Double::doubleValue) semble un peu cruelle - tout simplement le genre de "cérémonie" ordinaire que les lambdas et les ruisseaux étaient supposés se passer.

Les meilleures pratiques nous disent de préférer List instances aux tableaux, et pourtant, pour ce type de sommation, les tableaux semblent plus propres:

double[] vals = . . . ;
double sum = Arrays.stream(vals).sum();

Certes, on pourrait faire ceci:

List<Double> vals = . . . ;
double sum = vals.stream().reduce(0.0, (i,j) -> i+j);

Mais cette reduce(....) est tellement plus longue que sum().

Je comprends que cela a à voir avec la façon dont les flux doivent être adaptés aux primitives non-objets de Java, mais est-ce que quelque chose me manque? Existe-t-il un moyen de restreindre l'auto-sélection pour rendre cela plus court? Ou est-ce juste l'état de l'art actuel?


Mise à jour - Réponses Digest

Voici un résumé des réponses ci-dessous. Bien que j'ai un résumé ici, j'exhorte le lecteur à parcourir les réponses elles-mêmes dans leur intégralité.

@dasblinkenlight explique qu'une sorte de déballage sera toujours nécessaire, en raison de décisions prises plus loin dans l'histoire de Java, notamment en ce qui concerne la manière dont les génériques ont été implémentés et leur relation avec les primitives non-objets. Il note qu'il est théoriquement possible pour le compilateur d'intuitionner le décompression et de permettre un code plus court, mais cela n'a pas encore été implémenté.

@Holger montre une solution très proche de l'expressivité dont je parlais:

double sum = vals.stream().reduce(0.0, Double::sum);

Je n'étais pas au courant de la nouvelle méthode statique Double.sum(). Ajouté avec 1.8, il semble destiné au but même que je décrivais. J'ai aussi trouvé Double.min() et Double.max(). À l'avenir, j'utiliserai certainement cet idiome pour de telles opérations sur List<Double> Et similaires.

65
sparc_spread

Pour moi, la mapToDouble(Double::doubleValue) semble [ce que] les lambdas et les ruisseaux étaient supposés se passer de.

La nécessité d'utiliser mapToDouble découle de la décision d'implémenter des génériques via l'effacement de types, fermant la porte à toute possibilité d'utilisation de primitives à l'intérieur de génériques. C’est cette même décision qui a rendu nécessaire la création de la famille de classes DoubleStream, IntStream et LongStream - pour fournir un unboxing basé sur le flux.

Existe-t-il un moyen de restreindre l'auto-sélection pour rendre cela plus court? Ou est-ce juste l'état de l'art actuel?

Malheureusement, pas pour le moment: bien qu’il soit théoriquement possible pour le compilateur de comprendre que Stream<Double> Peut être converti implicitement en DoubleStream, de la même manière que les primitives, le été fait.

En ce qui concerne votre solution basée sur les baies, c'est la plus efficace des trois. Cependant, il n’est pas aussi flexible que les deux autres: celui avec mapToDouble vous permet de résumer tous les attributs d’une classe personnalisée, tandis que le dernier vous permet d’effectuer d’autres types d’agrégation.

reduce(....) est tellement plus long que sum()

Je conviens que cette approche est pire que mapToDouble en termes de lisibilité.

31
dasblinkenlight

Existe-t-il un moyen de restreindre l'auto-sélection pour rendre cela plus court?

Oui il y a. Vous pouvez simplement écrire:

double sum = vals.stream().mapToDouble(d->d).sum();

Cela rend le déballage implicite mais, bien entendu, n’ajoute pas à l’efficacité.

Depuis le Listest encadré, le déballage est inévitable. Une approche alternative serait:

double sum = vals.stream().reduce(0.0, Double::sum);

Il ne fait pas de mapToDouble mais permet quand même de lire le code en tant que "… somme".

47
Holger

Voici une autre façon de le faire. Si vous avez juste besoin d'une somme, moyenne, min, max etc. sur une liste de Double, Integer ou Long, vous pouvez utiliser l'un des Collectors, par exemple:

List<Double> doubles = Arrays.asList(3.14, 5.15, 4.12, 6.);
System.out.println(
        doubles.stream()
                .collect(Collectors.summingDouble(d -> d))
);

imprimerait 18.41

Remarque: le nom de la méthode est summingDouble. Il existe une autre méthode appelée summaryingDouble, qui renvoie DoubleSummaryStatistics, contenant tous les résultats des opérations mathématiques de base.

5
jFrenetic