web-dev-qa-db-fra.com

Java 8 Streams - Comparer les valeurs d'objet de deux listes et ajouter de la valeur au sous-objet de la première liste?

J'ai deux objets:

public class ObjectOne {

 private String id;
 private String name;
 private String school;
 private String score; //Default score is null

 ..getters and setters..
}

public class ObjectTwo {

 private String id;
 private String marks; 

 ..getters and setters..
}

Et, j'ai deux listes de l'objet ci-dessus,

List<ObjectOne> listOne;
List<ObjectTwo> listTwo;

Comment puis-je comparer deux listes et attribuer des notes de listTwo à score de listOne en fonction des critères si les ID sont égaux. Je sais, nous pouvons utiliser deux boucles for et le faire. Mais je veux l'implémenter en utilisant des flux Java8.

List<ObjectOne> result = new ArrayList<>();

for(ObjectOne one : listOne) {
    for(ObjectTwo two : listTwo) {
        if(one.getId().equals(two.getId())) {
            one.setScore(two.getmarks());
            result.add(one);
        }
    }
}
return result;

Comment puis-je implémenter cela en utilisant Java8 lambda et les flux?

2
Arpit Mathur

Soit listOne.size() est N et listTwo.size() est M. La solution 2-for-loops a alors une complexité de O (M * N).

Nous pouvons le réduire à O (M + N) en indexant listTwo par identifiants.

Cas 1 - en supposant que listTwo n'a pas d'objets avec le même identifiant

// pair each id with its marks
Map<String, String> marksIndex = listTwo.stream().collect(Collectors.toMap(ObjectTwo::getId, ObjectTwo::getMarks));
// go through list of `ObjectOne`s and lookup marks in the index
listOne.forEach(o1 -> o1.setScore(marksIndex.get(o1.getId())));

Cas 2 - en supposant que listTwo a des objets avec le même identifiant

    final Map<String, List<ObjectTwo>> marksIndex = listTwo.stream()
            .collect(Collectors.groupingBy(ObjectTwo::getId, Collectors.toList()));

    final List<ObjectOne> result = listOne.stream()
            .flatMap(o1 -> marksIndex.get(o1.getId()).stream().map(o2 -> {
                // make a copy of ObjectOne instance to avoid overwriting scores
                ObjectOne copy = copy(o1);
                copy.setScore(o2.getMarks());
                return copy;
            }))
            .collect(Collectors.toList());

Pour implémenter la méthode copy, vous devez soit créer un nouvel objet, copier les champs un à un, mais dans ce cas, je préfère suivre le modèle Générateur . Il en résulte également un code plus "fonctionnel".

3
Devstr

Cela devrait marcher. 

Map<String, String> collect = listTwo.stream().collect(Collectors.toMap(ObjectTwo::getId, ObjectTwo::getMarks));
listOne
   .stream()
   .filter(item -> collect.containsKey(item.getId()))
   .forEach(item -> item.setScore(collect.get(item.getId())));
0
pvpkiran

Après le code copie les marques d’ObjectTwo pour marquer dans ObjectOne. Si les deux ID sont égaux, il n’a pas d’objet intermédiaire List<ObjectOne> result

listOne.stream()
    .forEach(one -> {listTwo.stream()
        .filter(two -> {return two.getId().equals(one.getId());})
        .limit(1)
        .forEach(two -> {one.setScore(two.getMarks());});
    });
0
Pavan Kumar Jorrigala