本帖最后由 chslzj 于 2013-7-13 22:46 编辑
今天面试,面试老师问了这样一个问题,"Set里面的元素是怎样判断是否重复的?"我答,是通过hashcode方法和equals方法实现的(这里我的表述也有问题,Set自身也有hashcode和equals方法,但是这两个方法做的是Set与Set的比较,里面的元素应该是通过contains方法来判断是否相等,具体的实现应该是元素的hashcode与equals方法来判断的),我觉得面试老师心里面的答案应该不是这个,好吧,这个疑惑一直我心里,面试完了,来逛论坛,发现一帖子,”这几个Java基础面试题不错哟“,
里面的第十三条
13、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别 答:Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等 难道真的是iterator?iterator是迭代啊,迭代怎么判断啊????百度了一下,都是这个答案,我就不相信,我要探究竟!!! 我带着怀疑的态度去查API 首先,Set是一个借口类,这个毫无疑问,然后判断是否重复的方法可以从两个方法里面存在,一个是add(添加成功或者失败,因为是否重复),还有一个是contains(是否含有这个元素),但是Set类源码没有具体的实现过程,这里我就选取了HashSet这个实现类的源码来观察这个问题。 首先, - /**
- * Returns <tt>true</tt> if this set contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this set
- * contains an element <tt>e</tt> such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>.
- *
- * @param o element whose presence in this set is to be tested
- * @return <tt>true</tt> if this set contains the specified element
- */
- public boolean contains(Object o) {
- return map.containsKey(o);
- }
复制代码 这是hashSet的contains方法,它调用了一个map对象的方法,然后通过这个我们去看看这个map对象是什么吧- private transient HashMap<E,Object> map;
复制代码 下面HashSet的空的构造方法- public HashSet() {
- map = new HashMap<E,Object>();
- }
复制代码 看到这里,我想大家应该都明白了,HashSet其实就是一个包装后的HashMap,并且把HashSet里面的元素作为HashMap的Key值,现在我们去这个HashMap里面去看看他的containsKey方法是怎样实现的。- /**
- * Returns <tt>true</tt> if this map contains a mapping for the
- * specified key.
- *
- * @param key The key whose presence in this map is to be tested
- * @return <tt>true</tt> if this map contains a mapping for the specified
- * key.
- */
- public boolean containsKey(Object key) {
- return getEntry(key) != null;
- }
复制代码 好吧,又失败,它还是调用getEntry(key)方法,判断有没有得到一个元素来实现,继续找getEntry(key)方法- /**
- * Returns the entry associated with the specified key in the
- * HashMap. Returns null if the HashMap contains no mapping
- * for the key.
- */
- final Entry<K,V> getEntry(Object key) {
- int hash = (key == null) ? 0 : hash(key.hashCode());
- for (Entry<K,V> e = table[indexFor(hash, table.length)];
- e != null;
- e = e.next) {
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- }
- return null;
- }
复制代码 真像了,这个方法首先得到key的hashcode值,再hash一次,得到一个hash的table(table其实是一个Entry[]),然后遍历这个table,如果存在元素,判读元素的hashcode值是否是否与key的hashcode相等并且key指向了同一个元素或者里面的值可以通过equals方法返回真(不等于空我就不说了,感觉不好描述啊),然后选择返回对象e(成功找到对象,说明存在)或者返回null(就是不存在啦),好吧,到这里的结论是,HashSet底层的是由元素的hashcode方法与equals方法得到是否存在相同元素的,当然问题也不是这样结束了,我们还没有看Add方法呢
这是HashSet的Add方法 - /**
- * Adds the specified element to this set if it is not already present.
- * More formally, adds the specified element <tt>e</tt> to this set if
- * this set contains no element <tt>e2</tt> such that
- * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
- * If this set already contains the element, the call leaves the set
- * unchanged and returns <tt>false</tt>.
- *
- * @param e element to be added to this set
- * @return <tt>true</tt> if this set did not already contain the specified
- * element
- */
- public boolean add(E e) {
- return map.put(e, PRESENT)==null;
- }
复制代码 好吧,直接去找hashmap的put方法吧- /**
- * Associates the specified value with the specified key in this map.
- * If the map previously contained a mapping for the key, the old
- * value is replaced.
- *
- * @param key key with which the specified value is to be associated
- * @param value value to be associated with the specified key
- * @return the previous value associated with <tt>key</tt>, or
- * <tt>null</tt> if there was no mapping for <tt>key</tt>.
- * (A <tt>null</tt> return can also indicate that the map
- * previously associated <tt>null</tt> with <tt>key</tt>.)
- */
- public V put(K key, V value) {
- if (key == null)
- return putForNullKey(value);
- int hash = hash(key.hashCode());
- int i = indexFor(hash, table.length);
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- addEntry(hash, key, value, i);
- return null;
- }
复制代码 这里,如果找到了对于的Key值,就把新的值赋给了旧的值,然后返回了旧的值,上面HashSet判断是否添加成功是根据是否返回Null来判断的,这里显然不是NUll,所以是HashSet的添加返回结果是false,然后我们接着看HashMap的代码,如果for循环执行完了,还没有跳出这个方法,说明就不存在这个对象了,然后返回null,表示添加成功,结束了,这就证明了HashSet是根据元素的hashcode方法与equals方法来判断是否重复的。在开始看Set源码的时候,我就知道他们为什么说是根据iterator来判断是否有重复的了,下面贴源码
- /**
- * Returns <tt>true</tt> if this set contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this set
- * contains an element <tt>e</tt> such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>.
- *
- * @param o element whose presence in this set is to be tested
- * @return <tt>true</tt> if this set contains the specified element
- * @throws ClassCastException if the type of the specified element
- * is incompatible with this set (optional)
- * @throws NullPointerException if the specified element is null and this
- * set does not permit null elements (optional)
- */
- boolean contains(Object o);
- /**
- * Returns an iterator over the elements in this set. The elements are
- * returned in no particular order (unless this set is an instance of some
- * class that provides a guarantee).
- *
- * @return an iterator over the elements in this set
- */
- Iterator<E> iterator();
复制代码坑爹啊有木有,他们是不是草草看了这个源码,把iterator方法的说明看做是contains方法的说明了???!!!!,什么根据iterator方法啊??? 我知道我面试成绩应该很差了,面试的时候确实没有解释清楚,但是,我要跟你们讲,别信什么Set是根据iterator来判断是否重复了的屁话了。 -------------------------------------------------------------------------------------------------------------------- 好吧,如果我讲的有错误,请你们指出来,我虚心接受 ps.版主,手抖一抖,技术分,额
|