TreeSet java: comparación e igualdad

Me gustaría tener la lista de objetos ordenados con la propiedad ‘sort_1’. Pero cuando quiero eliminar, me gustaría usar la propiedad ‘id’. El siguiente código representa el problema.

package javaapplication1; import java.util.TreeSet; public class MyObj implements Comparable { public long sort_1; public long id; public MyObj(long sort, long id) { this.sort_1=sort; this.id=id; } @Override public int compareTo(MyObj other) { int ret = Long.compare(sort_1, other.sort_1); return ret; } public String toString() { return id+":"+sort_1; } public static void main(String[] args) { TreeSet lst=new TreeSet(); MyObj o1 = new MyObj(99,1); MyObj o2 = new MyObj(11,9); lst.add(o1); lst.add(o2); System.out.println(lst); MyObj o3 = new MyObj(1234, 1); //remove myObje with id 1 boolean remove=lst.remove(o3); System.out.println(lst); } } 

La salida de este código es:

 [9:11, 1:99] [9:11, 1:99] 

Necesito tener la lista ordenada ya que hago muchas adiciones a la lista. No quiero usar explícitamente ningún método de “clasificación”. Cuáles son mis opciones ?

EDITAR:

Mi requisito es tener: objetos con ‘id’ como únicos, pero puede haber objetos con un valor de ‘orden’ duplicado.

Creo que el problema que tiene es que está implementando Comparable , pero su implementación parece ser inconsistente con los iguales, y no ha implementado ningún método de igualdad. Es decir:

El orden natural para una clase C se dice que es consistente con igual si y solo si e1.compareTo (e2) == 0 tiene el mismo valor booleano que e1.equals (e2) para cada e1 y e2 de la clase C

En tu caso, cuando construyes estos tres objetos:

 MyObj o1 = new MyObj(99,1); MyObj o2 = new MyObj(11,9); MyObj o3 = new MyObj(1234, 1); 

Verá que o1.compareTo (o3) == -1, mientras que o1.equals (o3) == false.

Pero parece que quieres o1.equals (o3) == true.

Además, reconozca que TreeSet.add () devuelve falso si el objeto ya existe en el conjunto. Esta comprobación se basa en el método equals ().

Para remediar esto, anule Object.equals () y Object.hashCode () de modo que tengan en cuenta el campo MyObj.id, y continúen usando el campo sort_1 en el método compareTo () cuando no sean iguales.

 package javaapplication1; import java.util.TreeSet; public class MyObj implements Comparable { public long sort_1; public long id; public MyObj(long sort, long id) { this.sort_1 = sort; this.id = id; } @Override public int compareTo(MyObj other) { return (this.equals(other))? 0 : Long.compare(sort_1, other.sort_1); } @Override public boolean equals(Object obj) { MyObj other = (MyObj) obj; return this.id == other.id && this.sort_1 == other.sort_1; } @Override public int hashCode() { return (int) id; } public String toString() { return id + ":" + sort_1; } public static void main(String[] args) { TreeSet lst = new TreeSet(); MyObj o1 = new MyObj(99L, 1L); MyObj o2 = new MyObj(11L, 9L); MyObj o3 = new MyObj(1234L, 1L); MyObj o4 = new MyObj(1234L, 1L); System.out.println( "Adding o1: " + lst.add(o1)); System.out.println( "Adding o2: " + lst.add(o2)); System.out.println( "Adding o3: " + lst.add(o3)); System.out.println( "Adding o4: " + lst.add(o4)); System.out.println(lst); System.out.println("o1.compareTo(o3) : " + o1.compareTo(o3)); System.out.println("o1.equals(o3) : " + o1.equals(o3)); //remove myObje with id 1 boolean remove = lst.remove(o3); System.out.println(lst); } } 

Salida:

 Adding o1: true Adding o2: true Adding o3: true Adding o4: false [9:11, 1:99, 1:1234] o1.compareTo(o3) : -1 o1.equals(o3) : false [9:11, 1:99] 

Sólo por casualidad descubrí esto ayer también. Esto parece ser un artefacto de la implementación de TreeMap (que es lo que TreeSet usa para almacenar sus entradas).

TreeMap usa un árbol de búsqueda binario para almacenar los pares clave / valor, pero solo usa el Comparador dado (o la función de comparación si la clase clave implementa Comparable) para verificar la igualdad, como puede ver en este código de exxcerpt:

 final Entry getEntry(Object key) { // Offload comparator-based version for sake of performance if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable k = (Comparable) key; Entry p = root; while (p != null) { int cmp = k.compareTo(p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } return null; } 

Casi llamaría a esto un error (no realmente solucionable) ya que el JavaDoc de la interfaz comparable dice explícitamente que devolver 0 con la función compareTo no tiene que implicar “igualdad”:

Se recomienda encarecidamente, pero no es estrictamente obligatorio que (x.compareTo (y) == 0) == (x.equals (y)).

No podrá almacenar cosas en el TreeSet de la forma que desee. Recomiendo usar un HashMap normal o un LinkedHashMap y luego simplemente ordenar la salida cuando necesite ordenarla con Collections.sort .

Además de todo esto, siempre me resulta extraño implementar la interfaz Comparable. La mayoría de las cosas realmente no tienen un ordenamiento “natural” que es inmediatamente obvio. A veces, esto puede llevar a errores extraños (¡como este!), Por lo que normalmente siempre ordeno solo cuando lo necesito usando comparadores personalizados. ¡Java 8 hace que escribirlos también sea muy fácil!

Use Map objectsByIDs; para almacenar sus objetos de datos por id: objectsByIDs.put(id,myObjectInstance); . Luego, puede recuperarlos del mapa de esta forma MyObject o = objectsByIDs.get(id); Y elimínelo de ambos: objectsByIDs.remove(o); lst.remove(o) objectsByIDs.remove(o); lst.remove(o) .

Esto es realmente extraño. La documentación de TreeSet.remove () indica explícitamente que el método invoca a equals () para encontrar el argumento en el Conjunto. Sin embargo, el seguimiento de la stack para eliminar se ve así

 Thread [main] (Suspended (breakpoint at line 18 in MyObj)) MyObj.compareTo(MyObj) line: 18 MyObj.compareTo(Object) line: 1 TreeMap.getEntry(Object) line: not available TreeMap.remove(Object) line: not available TreeSet.remove(Object) line: not available MyObj.main(String[]) line: 45 

Incluso el uso de Comparator no funciona. Me parece que algunos desarrolladores inteligentes en Sun / Oracle / openJDK pensaron que hacer compareTo () == 0 es lo mismo que equals (). no lo es.

La única opción es utilizar una estructura de datos externa para verificar la igualdad como se sugirió, o hacer un ciclo usted mismo, encontrar el elemento que desea y eliminarlo.

EDIT: Ahora lo entiendo. buscan el elemento utilizando la búsqueda binaria, por eso comparan ().

Tener una lógica diferente para remove y ordenar en TreeSet es casi seguro que es imposible, e incluso si fuera posible, se rompería si lo TreeSet divertido. No hagas eso

Tratar de meterse con el comparador para que haga algo mágico es una idea terrible. Acepte que hay una y solo una noción de comparación que le importará a TreeSet . Cualquier cosa que desee hacer con otra noción de comparación no debería usar los métodos TreeSet para hacerlo.

Lo que puede hacer en su lugar es tener un removeId(int) especial removeId(int) que haga algo como

 void removeId(int id) { Iterator itr = set.iterator(); while (itr.hasNext()) { if (itr.next().id == id) { itr.remove(); break; } } }