Suppose I need TreeSet
with elements sorted with some domain logic. By this logic it doesn\'t matter order of some elements that doesn\'t equal so compare metho
Yes, as others said above, hashCode() is not secure to use here. But if you dont care about the ordering of objects that are equal in terms of o1.compareTo(o2) == 0, you could do something like:
public int compare(Foo o1, Foo o2) {
int res = o1.compareTo(o2);
if (res == 0 && !o1.equals(o2)) {
return -1;
}
return res;
}
int res = o1.compareTo(o2);
if(res == 0 || !o1.equals(o2)){
return o1.hashCode() - o2.hashCode();
}
Can be problematic, since if 2 objects are equal (i.e. in your res == 0
) then these 2 objects return the same hashcode. Hashcodes are not unique for every object.
Edit @Stas, The System.identityHashCode(Object x);
still won't help you. The reason is described on the javadoc:
Returns the same hash code for the given object as would be returned by the default method
hashCode()
, whether or not the given object's class overrideshashCode()
. The hash code for the null reference is zero.
Two points may be relevant and these are that the return in one situation is shown as -1 and this depends if a negative value is permitted in the function parameter variable or in the relevant country of use, and also if the method you are using is permitted. There are standard data arranging methods like selector or selection sort and the paper description or code is usually available from a national authority if a copy is not in your workplace. Using comparisons like greater than or less than can speed up the code and avoids the use of a direct comparison for equality by implied dropthrough to later script or code.
While this might work, it is far from being a best practice.
From the SortedSet docs:
Note that the ordering maintained by a sorted set (whether or not an explicit comparator is provided) must be consistent with equals if the sorted set is to correctly implement the Set interface. (See the Comparable interface or Comparator interface for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a sorted set performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the sorted set, equal. The behavior of a sorted set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
For objects that implement Comparable
, there should always be a consistency between the methods equals()
, hashcode()
and compareTo()
.
I'm afraid a SortedSet
is just not what you want, nor will a Guava MultiSet
be adequate (because it will not let you independently retrieve multiple equal items). I think what you need is a SortedList
. There is no such beast that I know of (maybe in commons-collections, but those are a bit on the legacy side), so I implemented one for you using Guava's ForwardingList as a base class. In short: this List delegates almost everything to an ArrayList
it uses internally, but it uses Collections.binarySearch()
in it's add()
method to find the right insertion position and it throws an UnsupportedOperationException
on all optional methods of the List
and ListIterator
interfaces that add or set values at a given position.
The Constructors are identical to those of ArrayList, but for each of them there is also a second version with a custom Comparator
. If you don't use a custom Comparator, your list elements need to implement Comparable
or RuntimeException
s will occur during sorting.
public class SortedArrayList<E> extends ForwardingList<E> implements
RandomAccess{
private final class ListIteratorImpl extends ForwardingListIterator<E>{
private final int start;
public ListIteratorImpl(final int start){
this.start = start;
}
@Override
public void set(E element){throw new UnsupportedOperationException();}
@Override
public void add(E element){throw new UnsupportedOperationException();}
@Override
protected ListIterator<E> delegate(){return inner.listIterator(start);};
}
private Comparator<? super E> comparator;
private List<E> inner;
public SortedArrayList(){this(null, null, null);}
@SuppressWarnings("unchecked")
private SortedArrayList(
final List<E> existing,
final Collection<? extends E> values,
final Comparator<? super E> comparator
){
this.comparator =
(Comparator<? super E>)
(comparator == null
? Ordering.natural()
: comparator );
inner = (
existing == null
? (values == null
? new ArrayList<E>(values)
: new ArrayList<E>()
)
: existing;
}
public SortedArrayList(final Collection<? extends E> c){
this(null, c, null);
}
public SortedArrayList(final Collection<? extends E> c,
final Comparator<? super E> comparator){
this(null, c, comparator);
}
public SortedArrayList(final Comparator<? super E> comparator){
this(null, null, comparator);
}
public SortedArrayList(final int initialCapacity){
this(new ArrayList<E>(initialCapacity), null, null);
}
public SortedArrayList(final int initialCapacity,
final Comparator<? super E> comparator){
this(new ArrayList<E>(initialCapacity), null, comparator);
}
@Override
public boolean add(final E e){
inner.add(
Math.abs(
Collections.binarySearch(inner, e, comparator)
) + 1,
e
);
return true;
}
@Override
public void add(int i, E e){throw new UnsupportedOperationException();}
@Override
public boolean addAll(final Collection<? extends E> collection){
return standardAddAll(collection);
}
@Override
public boolean addAll(int i,
Collection<? extends E> es){
throw new UnsupportedOperationException();
}
@Override
protected List<E> delegate(){ return inner; }
@Override
public List<E> subList(final int fromIndex, final int toIndex){
return new SortedArrayList<E>(
inner.subList(fromIndex, toIndex),
null,
comparator
);
}
@Override
public ListIterator<E> listIterator(){ return new ListIteratorImpl(0); }
@Override
public ListIterator<E> listIterator(final int index){
return new ListIteratorImpl(index);
}
@Override
public E set(int i, E e){ throw new UnsupportedOperationException(); }
}
If you have no specific expected ordering for any two given elements, but still want to consider them un-equal, then you have to return some specified ordering anyway.
As others have posted, hashCode()
isn't a good candidate, because the hashCode()
values of both elements can easily be equal. System.identityHashCode() might be a better choice, but still isn't perfect, as even identityHashCode()
doesn't guarantee unique values either
The Guava arbitrary() Ordering implements a Comparator
using System.identityHashCode()
.
Beware: even for two Foos f1
,f2
with f1 != f2
you could get f1.hashCode() == f2.hashCode()
! That means you won't get a stable sorting with your compare
Method.