2017-12-22 3 views
1

Ich habe ein Objekt, wie folgend:Überschneidung zwischen zwei HashSets in Java 8

public Class MyObjDTO { 
    private Long id; 
    private Boolean checked; 

    //getter and setters 

    @Override 
    public final int hashCode() { 
     Long id = getId(); 
     return (id == null ? super.hashCode() : id.hashCode()); 
    } 

    @Override 
    public boolean equals(final Object obj) { 
     if (this == obj) 
      return true; 
     if (!(obj instanceof MyObjDTO)) 
      return false; 
     Long id = getId(); 
     Long objId = ((MyObjDTO) obj).getId(); 
     if (id.equals(objId)) { 
      return true; 
     } else { 
      return false; 
     } 
    } 
} 

Und ich habe zwei Hash-Sets einige Beispiele aus diesem Objekt enthält:

HashSet oldSet = new HashSet(); 
oldSet.add(new MyObjDTO(1,true)); 
oldSet.add(new MyObjDTO(2,true)); 
oldSet.add(new MyObjDTO(3,false)); 

HashSet newSet = new HashSet(); 
newSet.add(new MyObjDTO(1,false)); 
newSet.add(new MyObjDTO(2,true)); 
newSet.add(new MyObjDTO(4,true)); 

Also, was ich tun möchte, hier ist es, Objekte auszuwählen, die in den newSet und nicht in den oldSet, in diesem Fall sind seine: new MyObjDTO(4,true), die ich dies habe mit:

Stream<MyObjDTO> toInsert = newSet.stream().filter(e -> !oldSet.contains(e)); 

Dann möchte ich Objekte auszuwählen, die in der oldSet sind und nicht in der newSet, seine in diesem Fall: new MyObjDTO(3,false), die ich tat dies mit:

Stream<MyObjDTO> toRemove = oldSet.stream().filter(e -> !newSet.contains(e)); 

Der letzte Schritt besteht darin, dass ich die Objekte auswählen möchten das ist in newSet und oldSet, aber sie haben einen anderen Wert für das Attribut checked, in diesem Fall ist es: new MyObjDTO(1,false). Was ich versucht, ist dies:

Stream<MyObjDTO> toUpdate = oldSet.stream().filter(newSet::contains); 

Aber dieser wird sowohl new MyObjDTO(1,false) und new MyObjDTO(2,true) zurück.

Wie kann ich das lösen?

+1

Was wird mich mit diesen Strömen tun? – Holger

+0

@Holger Einfügen, Entfernen oder Aktualisieren meiner Objekte in der Datenbank. –

+0

Brauchen Sie die Kreuzung zwischen zwei 'Set's? Warum nicht die alte und zuverlässige Methode 'retainAll' verwenden? Entsprechend 'Set' javadoc, wenn das Argument, das an die' retainAll'-Methode übergeben wird, ebenfalls eine Menge ist, ist das Ergebnis die Schnittmenge von ihnen. – STaefi

Antwort

1

Erstens Ihre equals() und hashCode() Methoden verletzen ihren Basisvertrag. Gemäß der javadoc of hashCode():

Wenn zwei Objekte das Gleichheits gemäß (Object) Verfahren gleich sind, dann ruft die Methode hashCode auf jeder der beiden Objekte müssen die gleiche ganzzahlige Ergebnis.

Ihre Implementierung von hashCode() folgt nicht diesem Vertrag. Ihr erster Schritt sollte sein, das zu beheben.

Zweitens, da Java 1.2 (fast 20 Jahre), java die Methode zur Verfügung gestellt hat removeAll() das genau das tut, was Sie für den ersten Teil tun:

// Given these 2 sets: 
HashSet<MyObjDTO> oldSet = new HashSet<>(); 
HashSet<MyObjDTO> newSet = new HashSet<>(); 

HashSet<MyObjDTO> onlyInNew = new HashSet<>(newSet); 
onlyInNew.removeAll(oldSet); 
// similar for onlyInOld 

Für den zweiten Teil, werden Sie müssen erstellen Map zu finden und nutzen Sie das Objekt aus:

Map<MyObjDTO, MyObjDTO> map = new HashMap<>O; 
oldSet.forEach(o -> map.put(o, o); 

HashSet<MyObjDTO> updated = new HashSet<>(newSet); 
updated.removeIf(o -> oldSet.contains(o) && o.getChecked()() != map.get(o).getChecked()); 
+0

Danke, aber ich konnte nicht herausfinden, wie meine Implementierung von 'hashCode()' nicht der 'hashCode()' Einschränkung folgt. –

+0

@IchigoKurosaki Sie haben Ihre Frage bearbeitet, seit ich sie gesehen habe und die Implementierung geändert habe; es war nur 'return super.hashCode();'. Es ist in Ordnung jetzt – Bohemian

+0

Danke für Ihre Hilfe –

1

Im letzten Schritt setzen Sie auf der equals() Methode des DTO:

Stream<FonctionnaliteDTO> toUpdate = oldSet.stream().filter(newSet::contains); 

Das Verfahren nutzen nur die ID field zu bestimmten Gegenstand Gleichheit.
Sie wollen das nicht tun. Sie möchten nach einem bestimmten Feld filtern: checked.

Außerdem sollten Sie die Operation auf das Ergebnis der Schnittmenge der beiden Sets durchführen.

Beachten Sie, dass Sie einfach Collection.retainAll() verwenden sollen, die Kreuzung zwischen zwei Sammlungen zu berechnen:

Set<MyObjDTO> set = ... 
Set<MyObjDTO> setTwo = ... 

set.retainAll(setTwo); 

Dann können Sie Objekte auswählen, die sowohl gleichen id und checked Wert durch eine doppelte Schleife verwenden: für + Iterator.

for (MyObjDTO dto : set){ 
    for (Iterator<MyObjDTO> it = set.iterator(); it.hasNext();){ 

     MyObjDTO otherDto = it.next(); 
     if (otherDto.getId().equals(dto.getId()) && 
      otherDto.getChecked() == dto.getChecked()){ 
     it.remove(); 
     } 
    } 
} 

Sie könnten das tun, mit Strom, aber IHMO es weniger lesbar sein könnte.

+0

Ich würde das lieber mit Stream machen. –

3

Eine Möglichkeit ist, zunächst eine Karte verwenden und dann Ihre Filterbedingung anpassen:

Map<MyObjDTO, Boolean> map = newSet.stream() 
    .collect(Collectors.toMap(Function.identity(), MyObjDTO::getChecked)); 

Stream<MyObjDTO> toUpdate = oldSet.stream() 
    .filter(old -> newSet.contains(old) && old.getChecked() != map.get(old)); 
+0

Was ist 'Funktion'? –

+1

@IchigoKurosaki 'Funktion' ist' java.util.function.Function' Schnittstelle. Sie können 'Function.identity()' durch 'a -> a' ersetzen. Es bedeutet, das selbe Objekt wie den Schlüssel der Karte zu belassen. –

+1

@FedericoPeraltaSchaffner Ich denke, dass Guave über "Sets # intersections" etwas Ähnliches hatte. – Eugene