3
votes

Comment puis-je transformer mon code à l'aide de l'API Java 8 Stream?

J'écris une méthode simple pour imprimer les statistiques d'une série de résultats de jeux. Chaque jeu contient une liste de résultats, qui contiennent des énumérations en fonction du résultat d'un jeu. Mon instructeur a commenté un TODO dans mon code:

public static void printStatistics(List<Game> games) {
    float win = 0;
    float lose = 0;
    float draw = 0;
    float all = 0;

    //TODO: should be implemented /w stream API
    for (Game g : games) {
        for (Outcome o : g.getOutcomes()) {
            if (o.equals(Outcome.WIN)) {
                win++;
                all++;
            } else if (o.equals(Outcome.LOSE)) {
                lose++;
                all++;
            } else {
                draw++;
                all++;
            }
        }
    }
    DecimalFormat statFormat = new DecimalFormat("##.##");

    System.out.println("Statistics: The team won: " + statFormat.format(win * 100 / all) + " %, lost " + statFormat.format(lose * 100 / all)
            + " %, draw: " + statFormat.format(draw * 100 / all) + " %");

}

Je connais les expressions lambda. J'ai essayé de chercher des solutions en ligne, mais je n'ai pas pu trouver d'exemples d'un flux accédant aux champs d'un champ d'une classe. Je serais heureux si vous pouviez me donner une solution ou me fournir un tutoriel pertinent. Merci.


5 commentaires

Pourquoi utiliser stream? Ne sera pas plus efficace car vous devez calculer plusieurs valeurs non directement liées à l'objet que vous manipulez


C'était aussi ma première question. Il n'y a aucun moyen que je puisse obtenir les 3 valeurs dont j'ai besoin avec seulement 1 pipline, non?


Pourquoi utilisez-vous des flottants pour les valeurs discrètes?


Si ce n'est que pour les calculs en virgule flottante, vous pouvez faire en sorte que les littéraux flottent à la place. Par exemple. gagner * 100f / tous


Merci d'avoir fait remarquer cela!


3 Réponses :


0
votes

Pour accéder aux champs de la classe dans un Stream , utilisez map:

games.stream().map(game -> 
  game.getOutcomes().stream().map(outcome -> {
    // do something with each `outcome`
  })
)

Le code ci-dessus suppose que getOutcomes () renvoie une List , qui n'est pas spécifiée dans la version actuelle de la question de OP.

Notez que vous ne pouvez pas simplement incrémenter les compteurs dans un flux , comme vous pourriez l'espérer, car toutes les variables utilisées dans les Stream doivent être finales ou effectivement définitives. Vous devrez creuser un peu plus dans l'API Stream pour déterminer comment incrémenter comme vous l'avez fait dans votre solution d'origine.

Conseil: vous voulez faire quelque chose comme this , qui utilise Collectors.groupingBy () .


0 commentaires

2
votes

groupingBy correspond à ce cas:

long all = map.values().stream()
    .mapToLong(Long::valueOf)
    .sum();

Et obtenez win , perd count: p >

long win = map.get(Outcomes.WIN);
long lose = map.get(Outcomes.LOSE);
...

Pour obtenir le compte all , vous devez additionner toutes les valeurs de la carte

Map<Outcome, Long> map = games.stream()
        .flatMap(game -> game.getOutcomes().stream())
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));


0 commentaires

5
votes

Vous pouvez diffuser des jeux, flatMap vers les résultats, puis les rassembler sur une carte de décompte:

Map<Outcome, Long> counts = games.stream()
        .map(Game::getOutcomes)
        .flatMap(Collection::stream)
        .collecting(Collectors.groupingBy(o -> o, Collectors.counting()));

long win = counts.getOrDefault(Outcome.WIN, 0L);
long lose = counts.getOrDefault(Outcome.LOSE, 0L);
long draw = counts.getOrDefault(Outcome.DRAW, 0L);
long all = games.stream()
        .mapToInt(g -> g.getOutcomes().size())
        .sum();


0 commentaires