比较器和equals()
问题内容:
假设我需要TreeSet
使用按某些域逻辑排序的元素。通过这种逻辑,不相等的元素的顺序无关紧要,因此compare方法可以返回0,但是在这种情况下,我不能将它们放入TreeSet
。
所以,问题是:这样的代码有什么缺点:
class Foo implements Comparable<Foo>{}
new TreeSet<Foo>(new Comparator<Foo>(){
@Override
public int compare(Foo o1, Foo o2) {
int res = o1.compareTo(o2);
if(res == 0 || !o1.equals(o2)){
return o1.hashCode() - o2.hashCode();
}
return res;
}
});
更新 :
好。如果它应该永远是方法之间的一致性equals()
,hashcode()
并且compareTo()
,作为@SPFloyd -
seanizer和其他人说。如果我删除Comparable
接口并移入此逻辑是否会更好甚至更好Comparator
(我可以在不破坏封装的情况下做到这一点)?因此它将是:
class Foo{}
new TreeSet<Foo>(new Comparator<Foo>(){
@Override
public int compare(Foo o1, Foo o2) {
//some logic start
if(strictliBigger(o1, o2)){ return 1;}
if(strictliBigger(o2, o1)){ return -1;}
//some logic end
if(res == 0 || !o1.equals(o2)){
return o1.hashCode() - o2.hashCode();
}
return res;
}
});
更新2 :
会System.identityHashCode(x)
比hashCode()
不需要稳定排序更好吗?
问题答案:
尽管这可能行得通,但这远非最佳实践。
请注意, 如果排序集要正确实现Set接口 ,则 排序集(无论是否提供显式比较器)所维护的顺序 必须与equals一致
。(有关与equals一致的精确定义,请参见Comparable接口或Comparator接口。)之所以这样,是因为Set接口是根据equals操作定义的,但是排序后的set使用其compareTo(或compare)方法执行所有元素比较。
,因此从排序集的角度来看,此方法认为相等的两个元素相等。即使排序顺序与equals不一致,也可以很好地定义排序集的行为。它只是不遵守Set接口的一般约定。
为了实现目标Comparable
,应该永远是方法之间的一致性equals()
,hashcode()
和compareTo()
。
恐怕a
SortedSet
并不是您想要的,番石榴也不MultiSet
足够(因为它不会让您独立地检索多个相等的项目)。我认为您需要的是一个SortedList
。我没有听说过这样的野兽(也许是在commons-
collection中,但是在传统方面有点),因此我使用Guava的ForwardingList作为基类为您实现了一个。简而言之:此List几乎将所有内容委托给ArrayList
它在内部使用,但它Collections.binarySearch()
在其add()
方法中使用以找到正确的插入位置,并且UnsupportedOperationException
在List
和ListIterator
接口的所有可选方法上抛出,它们在给定位置添加或设置值。
构造函数与构造函数相同ArrayList
,但每个构造函数都有一个带有自定义的第二个版本Comparator
。如果您不使用自定义比较器,则列表中的元素需要实现,Comparable
否则RuntimeException
将在排序过程中发生。
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(); }
}