java中compareTo和equals的优先级 javaequalscompareTo 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 TreeSet是用TreeMap来实现的,看TreeMap的apipublic class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, Serializable基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。 此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。 注意,如果要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator)。这是因为 Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然是 定义良好的,只不过没有遵守 Map 接口的常规协定。 注意,此实现不是同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,则其必须 外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与现有键关联的值不是结构上的修改。)这一般是通过对自然封装该映射的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSortedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示: SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));collection(由此类所有的“collection 视图方法”返回)的 iterator 方法返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。 注意,迭代器的快速失败行为无法得到保证,一般来说,当存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。 此类及其视图中的方法返回的所有 Map.Entry 对都表示生成它们时的映射关系的快照。它们不 支持 Entry.setValue 方法。(不过要注意的是,使用 put 更改相关映射中的映射关系是有可能的。) 此类是 Java Collections Framework 的成员。 根据api的说法,只要compareTo相等两个对象就是相等的,哪怕equals 不相等。 根据api的说,结论只有一个,compareTo相等两个对象就是相等的,哪怕equals 不相等。 但是为什么呢,我还是云里雾里的,于是我去查源码了,TreeSet的add方法: public boolean add(E paramE) { return this.m.put(paramE, PRESENT) == null; }其中this.m为TreeMap,其put方法定义如下:public V put(K paramK, V paramV) { Entry localEntry1 = this.root; if (localEntry1 == null) { compare(paramK, paramK); this.root = new Entry(paramK, paramV, null); this.size = 1; this.modCount += 1; return null; } Comparator localComparator = this.comparator; Entry localEntry2; int i; if (localComparator != null) { do { localEntry2 = localEntry1; i = localComparator.compare(paramK, localEntry1.key); if (i < 0) { localEntry1 = localEntry1.left; } else if (i > 0) { localEntry1 = localEntry1.right; } else { return localEntry1.setValue(paramV); } } while (localEntry1 != null); } else { if (paramK == null) { throw new NullPointerException(); } localObject = (Comparable)paramK; do { localEntry2 = localEntry1; i = ((Comparable)localObject).compareTo(localEntry1.key); if (i < 0) { localEntry1 = localEntry1.left; } else if (i > 0) { localEntry1 = localEntry1.right; } else { return localEntry1.setValue(paramV); } } while (localEntry1 != null); } Object localObject = new Entry(paramK, paramV, localEntry2); if (i < 0) { localEntry2.left = ((Entry)localObject); } else { localEntry2.right = ((Entry)localObject); } fixAfterInsertion((Entry)localObject); this.size += 1; this.modCount += 1; return null; }重点在这:do { localEntry2 = localEntry1; i = ((Comparable)localObject).compareTo(localEntry1.key); if (i < 0) { localEntry1 = localEntry1.left; } else if (i > 0) { localEntry1 = localEntry1.right; } else { return localEntry1.setValue(paramV); } } while (localEntry1 != null);从上面的代码看,它根本就不管你什么equals方法,只使用了compareTo方法来查找将要添加进来的元素在集合中的位置,当小于0则放在左边,大于0则放在右边,等于0就放在当天位置.所以结论就是:treeset的add方法只使用compareTo来确定添加进来的元素在集合中的位置,与equals方法毛关系都没有. Comparable 和Comparator主要是排序, 相等主要是依靠equals 和 hashCode 方法 他们是 JAVA集合的基础, 在他们上面才有了,所谓的 排序比较, SET 的不允许添加重复的元素你定义的equals方法是不起作用的,在这里, 因为你没有定义hashCode方法 排序只根据compareTo方法比较,比较的是大小,而equals是指是否相同.相等未必是相同. 文档说,应该自己保证它们之间的语义逻辑是相同的,不应该出现矛盾的结果。比如 compareTo 不是0,但 equals 是 true 就不合适。 Worker对象是作为键值对存储在TreeMap里,并且元素不能重复。你重写了equals方法也要重写hashCode方法。 Java编码问题 类与接口转型的问题 哪里下载JAVA的编译环境 关于"接口"的理解,正确吗?请多多指教:-(... 如何設定Thread每天06:00去run一次呢 新手求教 怎么知道Jtable中的数值变化了? 打包 Vector的问题。 关于rmi的问题 金山笔试,多线程问题???在线急等 ConcurrentHashMap能否被手动加锁来执行独占式访问
public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, Serializable基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。 此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。 注意,如果要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator)。这是因为 Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然是 定义良好的,只不过没有遵守 Map 接口的常规协定。 注意,此实现不是同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,则其必须 外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与现有键关联的值不是结构上的修改。)这一般是通过对自然封装该映射的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSortedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示: SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));collection(由此类所有的“collection 视图方法”返回)的 iterator 方法返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。 注意,迭代器的快速失败行为无法得到保证,一般来说,当存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。 此类及其视图中的方法返回的所有 Map.Entry 对都表示生成它们时的映射关系的快照。它们不 支持 Entry.setValue 方法。(不过要注意的是,使用 put 更改相关映射中的映射关系是有可能的。) 此类是 Java Collections Framework 的成员。
TreeSet的add方法:
public boolean add(E paramE)
{
return this.m.put(paramE, PRESENT) == null;
}
其中this.m为TreeMap,其put方法定义如下:
public V put(K paramK, V paramV)
{
Entry localEntry1 = this.root;
if (localEntry1 == null)
{
compare(paramK, paramK);
this.root = new Entry(paramK, paramV, null);
this.size = 1;
this.modCount += 1;
return null;
}
Comparator localComparator = this.comparator;
Entry localEntry2;
int i;
if (localComparator != null)
{
do
{
localEntry2 = localEntry1;
i = localComparator.compare(paramK, localEntry1.key);
if (i < 0) {
localEntry1 = localEntry1.left;
} else if (i > 0) {
localEntry1 = localEntry1.right;
} else {
return localEntry1.setValue(paramV);
}
} while (localEntry1 != null);
}
else
{
if (paramK == null) {
throw new NullPointerException();
}
localObject = (Comparable)paramK;
do
{
localEntry2 = localEntry1;
i = ((Comparable)localObject).compareTo(localEntry1.key);
if (i < 0) {
localEntry1 = localEntry1.left;
} else if (i > 0) {
localEntry1 = localEntry1.right;
} else {
return localEntry1.setValue(paramV);
}
} while (localEntry1 != null);
}
Object localObject = new Entry(paramK, paramV, localEntry2);
if (i < 0) {
localEntry2.left = ((Entry)localObject);
} else {
localEntry2.right = ((Entry)localObject);
}
fixAfterInsertion((Entry)localObject);
this.size += 1;
this.modCount += 1;
return null;
}
重点在这:
do
{
localEntry2 = localEntry1;
i = ((Comparable)localObject).compareTo(localEntry1.key);
if (i < 0) {
localEntry1 = localEntry1.left;
} else if (i > 0) {
localEntry1 = localEntry1.right;
} else {
return localEntry1.setValue(paramV);
}
} while (localEntry1 != null);
从上面的代码看,它根本就不管你什么equals方法,只使用了compareTo方法来查找将要添加进来的元素在集合中的位置,当小于0则放在左边,大于0则放在右边,等于0就放在当天位置.
所以结论就是:treeset的add方法只使用compareTo来确定添加进来的元素在集合中的位置,与equals方法毛关系都没有.
相等未必是相同.