6
votes

java streams: réduction simple

J'ai un flux de MetricGroup , où:

Stream.of(new MetricGroup(1,100), new MetricGroup(1,200))
    .reduce(????);

Je dois résumer toutes les métriques dans une seule métrique. Je veux dire, je dois ajouter tous les metric.uploadedDocs dans un sumMetric.uploadedDocs et metric.uploadedKds dans un sumMetric.uploadedKbs .

Je suppose que j'ai besoin d'une sorte de reduce

public class MetricGroup {

    private int uploadedDocs;
    private long uploadedKbs;

    // getters and setters

}

Des idées?


4 commentaires

Voulez-vous en modifier un existant ou devez-vous créer un nouveau MetricGroup pour le résultat?


À mon humble avis, il serait plus simple et plus facile à comprendre et à maintenir à utiliser deux simples réduction / somme au lieu d'un trop compliqué.


@tobias_k bon cri en effet !, j'avais une idée similaire en lisant ceci (posté une réponse ci-dessous). j'espère que ça vous va :)


Veuillez envisager d'accepter une réponse. Ou avez-vous autre chose à demander?


5 Réponses :


7
votes

Vous pouvez utiliser cette surcharge de réduire :

private static MetricGroup combine(MetricGroup x, MetricGroup y) {
    x.setUploadedDocs(x.getUploadedDocs() + y.getUploadedDocs());
    x.setUploadedKbs(x.getUploadedKbs() + y.getUploadedKbs());
    return x;
}

// ....

.collect(() -> new MetricGroup(0, 0),
    YourClass::combine,
    YourClass::combine
)

comme ceci:

.reduce(new MetricGroup(0, 0),
        (x, y) -> new MetricGroup(
                      x.getUploadedDocs() + y.getUploadedDocs()
                      x.getUploadedKbs() + y.getUploadedKbs()
                  )
        )


2 commentaires

Je pense que c'est la meilleure solution car elle ne manipule pas les MetricGroups existants


@FedericoPeraltaSchaffner Vous avez raison. Je pensais à collect .



2
votes

Il suffit de passer un seul lambda (manipulera MetricGroup existant)

Stream.of(new MetricGroup(1, 100), new MetricGroup(1, 200))
        .reduce((a, b) -> new MetricGroup(a.getUploadedDocs() + b.getUploadedDocs(), a.getUploadedKbs() + b.getUploadedKbs()));

Ou, pour vraiment obtenir un nouveau MetricGroup (sans en manipuler un existant)

Stream.of(new MetricGroup(1, 100), new MetricGroup(1, 200))
    .reduce((a, b) -> {
      a.setUploadedDocs(a.getUploadedDocs() + b.getUploadedDocs());
      a.setUploadedKbs(a.getUploadedKbs() + b.getUploadedKbs());
      return a;
    });

// Optional[F.MetricGroup(uploadedDocs=2, uploadedKbs=300)]


0 commentaires

3
votes

Pour éviter la création de plusieurs / nombreux objets MetricGroup lors de l'appel reduction , vous pouvez effectuer deux appels séparés pour additionner les UploadedDocs et UploadedKbs , puis construisez un nouveau MetricGroup représentant le résultat.

int uploadedDocsSum = source.stream().mapToInt(MetricGroup::getUploadedDocs).sum();
long uploadedKbsSum = source.stream().mapToLong(MetricGroup::getUploadedKbs).sum();
MetricGroup result = new MetricGroup(uploadedDocsSum, uploadedKbsSum);

Peut-être aussi plus lisible ...


4 commentaires

Dans le pire des cas, c'est juste un objet créé avec les autres approches. Vous avez également deux flux tandis que les autres solutions fonctionnent avec un seul. Mais le vôtre est probablement plus lisible.


À mon humble avis, le seul problème avec cette approche pourrait être si OP n'a qu'une référence à cette instance de flux et qu'elle est trop grande pour être mise en mémoire tampon dans une liste.


@tobias_k mon mauvais, je travaillais sur un autre problème sur mon IDE et j'ai oublié de l'adapter ici. édité maintenant.


@ user489872 D'un autre côté, les autres approches ont 2 * n appels aux getters et un autre n appels aux setters, alors que cela ne fait que n appels au getter.



2
votes

Comme toujours avec les flux java, vous n'avez pas vraiment besoin de les utiliser. Je suggère de créer une méthode d'aide à la réduction simple:

MetricGroup reduced = reduce(stream::iterator);

Si vous ne pouvez pas changer cela, vous commencez avec un flux, vous pouvez toujours utiliser la méthode ci-dessus, en passant simplement une référence au Méthode Stream.iterator () :

public static MetricGroup reduce(Iterable<? extends MetricGroup> metrics){
   int uploadedDocs = 0;
   long uploadedKbs = 0L;
   for(MetricGroup metric : metrics){
       uploadedDocs += metric.getUploadedDocs();
       uploadedKbs += metric.getUploadedKbs();
   }
   return new MetricGroup(uploadedDocs, uploadedKbs);
}


0 commentaires

1
votes

Si vous souhaitez utiliser la réduction, je vous recommande de faire de MetricGroup un type de valeur, en rendant les champs finaux, en ajoutant un zéro et en remplaçant les setters par des méthodes de combinaison.

MetricGroup sum = metricGroups.stream()
                              .reduce(MetricGroup.ZERO, MetricGroup::add);

Cela vous permettra d'effectuer des opérations de flux correctement:

public class MetricGroup {
    private final int uploadedDocs;
    private final long uploadedKbs;

    // obvious constructor
    // getters

    public static final ZERO = new MetricGroup(0, 0);

    public MetricGroup add(MetricGroup a, MetricGroup b) {
        return new MetricGroup(a.uploadedDocs + b.upLoadedDocs,
                               a.uploadedKbs + b.uploadedKbs);
    }

    public MetricGroup uploadOneDoc(long kbs) {
        return new MetricGroup(uploadedDocs + 1, uploadedKbs + kbs);
    }
}


1 commentaires

Bonne réponse, j'aime beaucoup le groupe de métriques constant ZERO