2013-06-06 13 views
6

Ho aggiunto alcuni oggetti semplici a un TreeSet, ma quando chiamo i metodi remove() e contains() di TreeSet, non funzionano. Tuttavia, quando eseguo un'iterazione sull'insieme, l'oggetto viene stampato. Gli oggetti dei dipendenti devono essere aggiunti all'insieme mentre l'unicità degli oggetti si basa sulla proprietà del nome dell'oggetto. La proprietà Id è il valore che deve essere ordinato, ma che non è univoco.Java TreeSet: remove e contains() non funziona

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

No, ho fatto prima, ma ha commentato che in un secondo momento, quindi questo non può essere il reaosn. – user1812379

+0

@fge certo, hai ragione, ho trascurato questo. –

+0

OK, vedere la soluzione, e una cosa che dovete tenere a mente: non si può fare ciò che si vuole sia con '.equals()' /'.hashCode() 'o da soli un' Comparator' - non si può avere la vostra sia torta e mangiarlo. Il mio esempio "BigDecimal" avrebbe già dovuto darti un suggerimento! – fge

risposta

21

A TreeSet inserti/rimuove in base ai risultati di Comparable, non .equals()/.hashCode()!

Ciò significa, BTW, che gli oggetti del vostro Set implementano Comparable (se così non fosse, ogni volta che ci si è tentato e inserito un elemento, che ci si sono stati accolti con un ClassCastException).

Per essere più precisi, TreeSet è un'implementazione di SortedSet.

Se si desidera un set .equals()/.hashCode()-compatibile, utilizzare, ad esempio, un HashSet.

Per l'illustrazione, qui è quello che succede con BigDecimal (pubblicato poche ore fa 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 

per citare il javadoc per Comparable, vuol dire che BigDecimal s' .compareTo() è "non è coerente con .equals() ".

** EDIT ** Per quanto riguarda ciò che il PO vuole:

  • un Collection che non accetterà nomi duplicati;
  • una vista ordinata di quello Collection che selezionerà contro l'id dell'utente.

Come sopra menzionato, non è possibile avere una raccolta che sia sia. La soluzione:

  • per il primo, a HashSet;
  • per il secondo, una copia di tale serie in un ArrayList, quindi utilizzando Collections.sort().

Ciò significa .equals() e .hashCode() devono agire solo sul nome, mentre un costume Comparator agirà sulla id. Lo Comparator non ha altra scelta che essere personalizzato poiché si tratta di un comparatore che non è in alcun modo compatibile con .equals().

Per quanto riguarda il codice proposto, ci sono problemi.

Primo: Employee annullamenti .equals() ma non .hashCode(). Pertanto, Employee interrompe il contratto .equals() (una parte della quale è che se due oggetti sono uguali, devono avere lo stesso codice hash). Per di più, .hashCode() è fondamentale per il HashSet per funzionare a tutti.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); 
} 

Secondo: il comparatore è altrettanto rotti a Employee poiché rompe il Comparator contratto (per qualsiasi o1 e 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; 
    } 
} 

Poi, come ottenere una copia ordinato del set:

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

FATTO.

+2

+1, buona scoperta. Le risposte predefinite funzionano solo nel 95% delle domande;) – jlordo

+0

@fge Poi non ha funzionato perché non ho implementato uguaglianza nella compareTo(), perché volevo permettere duplicati. Per un ordine di oggetti come – user1812379

+0

@ user1812379 '.compareTo()' dovrebbe implementare ordinamento totale, non l'uguaglianza – fge

3

Il tuo problema è concettuale.

Se si desidera una raccolta differenziata di oggetti unici: TreeSet
Se si desidera una raccolta differenziata erano diversi oggetti possono avere lo stesso valore di confronto ai fini di ordinamento: PriorityQueue

Per inciso, i metodi in un PriorityList sono più adatto alle solite esigenze del secondo caso rispetto a quelle su TreeSet. Ero solito pensarlo come un difetto di TreeSet. Ad esempio, per estrarre il primo oggetto dalla raccolta.

Speranza che aiuta :-)

Problemi correlati