2013-06-06 13 views
6

Dodałem kilka prostych obiektów do zestawu TreeSet, ale gdy zadzwonię do metod usuwania() i zawiera() TreeSet, nie działają. Jednak po przejściu przez zestaw obiekt zostanie wydrukowany. Obiekty pracownicze zostaną dodane do zestawu, podczas gdy unikalność obiektów jest oparta na właściwości nazwy obiektów. Właściwość Id to wartość, którą należy posortować, ale która nie jest unikalna.Java TreeSet: remove and contains() does not working

public class Employee { 
    private String name; 
    private int id; 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public String getName() { 
    return name; 
    } 

    public void setName(String name) { 
    this.name = name; 
    } 

// Two objects are considered equal if their names are equal 
    @Override 
    public boolean equals(Object o) { 
    if (o == null) 
     return false; 
    if (this == o) 
     return true; 
    if (o.getClass() == this.getClass()) { 
     Employee p = (Employee) o; 
     if (p.getName() != null && this.getName() != null) 
     return this.getName().equals(p.getName()); 
     else 
     return false; 
    } else { 
     return false; 
    } 
    } 
} 

//******************************************************* 

public class EmployeeComp implements Comparator<Employee> { 

    // Sort Ids, but allow duplicates, hence this function is never returning 0 
    @Override 
    public int compare(Employee p1, Employee p2) { 
    int re = 0; 

    boolean scoreLt = (p1.getId() > p2.getId()); 
    boolean scoreGt = (p1.getId() < p2.getId()); 

    if(scoreLt) 
     re = -1; 
    if(scoreGt) 
     re = 1; 
    else 
     re = -1;      
     return re;     
    }  
} 
//******************************************************* 
// Collection shall store unique names with ordered values like: 
// Alex, 923 
// Toni, 728 
// Eddi, 232 
// Peter, 232 
// Eddi, 156 *** not allowed 
import java.util.TreeSet; 


public class Main { 
    private static EmployeeComp comp = new EmployeeComp(); 
    private static TreeSet<Employee> employees = new TreeSet<Employee>(comp); 

    public static void main(String[] args) { 

    Employee p1 = new Employee(); 
    p1.setName("Eddi"); 
    p1.setId(232); 

    Employee p2 = new Employee(); 
    p2.setName("Toni"); 
    p2.setId(728); 

    Employee p3 = new Employee(); 
    p3.setName("Peter"); 
    p3.setId(232); 

    Employee p4 = new Employee(); 
    p4.setName("Alex"); 
    p4.setId(923); 

    employees.add(p1); 
    employees.add(p2); 
    employees.add(p3); 
    employees.add(p4); 

    // Here, contains() and remove() should check the object address 
    // and not perform their actions based on compareTo 

     } 
} 
+0

Nie, zrobiłem to pierwszy, ale zauważył go później, więc to nie może być reaosn. – user1812379

+0

@ upewnij się, prawda, przeoczyłem to. –

+0

OK, zobacz rozwiązanie i jedną rzecz, o której musisz pamiętać: nie możesz zrobić tego, czego chcesz, używając '.equals()'/'.hashCode() 'lub samego' Comparator' - nie możesz mieć obu ciasto i zjedz to. Mój przykład "BigDecimal" powinien już dać ci wskazówkę! – fge

Odpowiedz

21

A TreeSet wkładek/usuwa według wyników Comparable, a nie .equals()/.hashCode()!

To oznacza, BTW, że przedmioty z twoich Set zrobić wdrożyć Comparable (jeśli nie, za każdym razem chcesz próbowali i wstawiony element, to że zostały witani z ClassCastException).

Aby być dokładniejszym, TreeSet jest implementacją SortedSet.

Jeśli chcesz zestaw zgodny z /.hashCode(), użyj, na przykład, HashSet.

Dla ilustracji, tutaj jest to, co dzieje się z BigDecimal (pisał kilka godzin temu here):

final BigDecimal one = new BigDecimal("1"); 
final BigDecimal oneDotZero = new BigDecimal("1.0"); 

final Set<BigDecimal> hashSet = new HashSet<>(); 
// BigDecimal implements Comparable of itself, so we can use that 
final Set<BigDecimal> treeSet = new TreeSet<>(); 

hashSet.add(one); 
hashSet.add(oneDotZero); 
// hashSet's size is 2: one.equals(oneDotZero) == false 

treeSet.add(one); 
treeSet.add(oneDotZero); 
// treeSet's size is... 1! one.compareTo(oneDotZero) == 0 

Cytując javadoc dla Comparable, oznacza to, że BigDecimal „s .compareTo()„nie jest zgodne z .equals() ".

** EDIT ** Zgodnie z tym, co PO chce:

  • Collection który nie zaakceptuje zduplikowane nazwy;
  • posortowany widok tego Collection, który będzie sortował według identyfikatora użytkownika.

Jak wspomniano powyżej, nie można mieć jednej kolekcji, która ma zarówno. Rozwiązanie:

  • dla pierwszego, HashSet;
  • na sekundę, kopię tego zestawu na ArrayList, a następnie za pomocą Collections.sort().

Oznacza to .equals() i .hashCode() musi działać tylko w nazwie, natomiast zwyczaj Comparator będzie działać na id. Comparator nie ma innego wyjścia, jak tylko być niestandardowym, ponieważ jest to komparator, który nie jest zgodny z .equals() w żadnym wypadku.

Co do proponowanego kodu, występują problemy.

Po pierwsze: Employee zastępuje .equals(), ale nie .hashCode(). Jako taki, Employee łamie umowę .equals() (z których jedna część jest taka, że ​​jeśli dwa obiekty są równe, muszą mieć ten sam kod skrótu). Co więcej, .hashCode() ma krytyczne znaczenie, aby w ogóle zadziałał HashSet.Fix:

@Override 
public int hashCode() 
{ 
    return name == null ? 0 : name.hashCode(); 
} 

@Override 
public boolean equals(final Object obj) 
{ 
    if (obj == null) 
     return false; 
    if (this == obj) 
     return false; 
    if (!(obj instanceof Employee)) 
     return false; 
    final Employee other = (Employee) obj; 
    return name == null ? other.name == null 
     : name.equals(other.name); 
} 

drugie: komparator jest równie uszkodzony jak Employee ponieważ łamie umowę Comparator (dla dowolnego o1 i o2, o1.compareTo(o2) == - o2.compareTo(o1)). Fix:

public final class EmployeeComp 
    implements Comparator<Employee> 
{ 
    @Override 
    public int compare(final Employee o1, final Employee o2) 
    { 
     final int id1 = o1.getId(), id2 = o2.getId(); 
     if (id1 == id2) 
      return 0; 
     return id1 > id2 ? 1 : -1; 
    } 
} 

Potem, jak uzyskać posortowanych kopii zestawu:

// "set" contains the unique employees 
final List<Employee> sorted = new ArrayList<Employee>(set); 
Collections.sort(list, new EmployeeComp()); 

zrobić.

+2

+1, good find. Domyślne odpowiedzi działają tylko w 95% pytań;) – jlordo

+0

@fge Wtedy to nie działało, ponieważ nie zaimplementowałem równości w compareTo(), ponieważ chciałem zezwolić na duplikaty. Dla porządku obiektów takich jak – user1812379

+0

@ user1812379 '.compareTo()' ma za zadanie implementować porządkowanie całkowite, a nie równość – fge

3

Twój problem jest pojęciowy.

Jeśli chcesz posortowaną kolekcję unikalnych przedmiotów: TreeSet
Jeśli chcesz sortowane kolekcji były różne obiekty mogą mieć taką samą wartość porównawczą dla celów Sortowanie: PriorityQueue

Nawiasem mówiąc, metody w PriorityList są bardziej dopasowane do zwykłych potrzeb drugiego przypadku niż te w TreeSet. Kiedyś uważałem to za niedociągnięcia TreeSet. Na przykład, aby pobrać pierwszy element z kolekcji.

nadzieję, że pomoże :-)

Powiązane problemy