2015-06-16 22 views
19

Mam klasę Java Parent z 20 atrybutami (attrib1, attrib2 .. attrib20) i odpowiadającymi jej obiektami pobierającymi i ustawiającymi. Mam również dwie listy obiektów Parent: list1 i list2.Scalanie dwóch list obiektów w java 8

Teraz chcę scalić obie listy i unikać powielania obiektów na podstawie attrib1 i attrib2.

Korzystanie Java 8:

List<Parent> result = Stream.concat(list1.stream(), list2.stream()) 
       .distinct() 
       .collect(Collectors.toList()); 

Ale w którym miejscu muszę określić atrybuty? Czy powinienem zastąpić metodę hashCode i equals?

+0

Użyj opcji Ustaw, aby uniknąć powtarzania wartości. – Noundla

+1

Zobacz http://stackoverflow.com/questions/27870136/java-lambda-stream-distinct-on-arbitrary-key/27872086 –

+1

Ciekawa funkcja echa, którą tam masz :) – CKing

Odpowiedz

14

Jeśli chcesz realizować equals i hashCode, miejscem na to jest wewnątrz klasa Parent. W ramach tej klasy dodać metody jak

@Override 
    public int hashCode() { 
     return Objects.hash(getAttrib1(), getAttrib2(), getAttrib3(), 
      // … 
          getAttrib19(), getAttrib20()); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if(this==obj) return true; 
     if(!(obj instanceof Parent)) return false; 
     Parent p=(Parent) obj; 
     return Objects.equals(getAttrib1(), p.getAttrib1()) 
      && Objects.equals(getAttrib2(), p.getAttrib2()) 
      && Objects.equals(getAttrib3(), p.getAttrib3()) 
      // … 
      && Objects.equals(getAttrib19(), p.getAttrib19()) 
      && Objects.equals(getAttrib20(), p.getAttrib20()); 
    } 

Jeśli to zrobił, distinct() powoływać na Stream<Parent> automatycznie zrobić dobry uczynek.


Jeśli nie chcą (lub nie mogą) zmienić klasę Parent, nie ma mechanizmu delegacja dla równości, ale można uciec się do zamawiania jako że posiada mechanizm delegacji:

Comparator<Parent> c=Comparator.comparing(Parent::getAttrib1) 
     .thenComparing(Parent::getAttrib2) 
     .thenComparing(Parent::getAttrib3) 
     // … 
     .thenComparing(Parent::getAttrib19) 
     .thenComparing(Parent::getAttrib20); 

Definiuje zamówienie na podstawie właściwości. Wymaga, aby typy samych atrybutów były porównywalne. Jeśli masz taką definicję, można go używać do wdrożenia odpowiednik distinct(), w oparciu o które Comparator:

List<Parent> result = Stream.concat(list1.stream(), list2.stream()) 
     .filter(new TreeSet<>(c)::add) 
     .collect(Collectors.toList()); 

Istnieje również wariant bezpieczny wątku, w przypadku, gdy chcesz go używać z równoległych strumieni :

List<Parent> result = Stream.concat(list1.stream(), list2.stream()) 
     .filter(new ConcurrentSkipListSet<>(c)::add) 
     .collect(Collectors.toList()); 
0

Zastąp metody equals i hashCode w klasie Parent, aby uniknąć duplikatów z list. Daje to dokładny wynik, jaki chcesz.

+4

To jest zła rada. Nigdy nie nadpisuj 'equals' bez nadpisywania' hashCode'. – Marvin

+0

http://stackoverflow.com/questions/2265503/why-do-i-need-to-override-the-equals-and-hashcode-methods-in-java właśnie czytam to i dowiedziałem się, dlaczego ,,, dzięki :) –

0

Jeśli chcesz zastąpić .equals(…) i .hashCode(), musisz to zrobić na klasie Parent. Zauważ, że może to spowodować, że inne zastosowania Parent się nie powiodą. Połączone rozwiązanie Alexisa C. jest bardziej konserwatywne.

0

Na przykład:

public class Parent { 

    public int no; 
    public String name; 

    @Override 
    public int hashCode() { 
     return (no << 4)^name.hashCode(); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (!(obj instanceof Parent)) 
      return false; 
     Parent o = (Parent)obj; 
     return this.no == o.no && this.name.equals(o.name); 
    } 
}