40个Java集合面试问题和答案
2015-05-21 21:53
417 查看
转自importNew :http://www.importnew.com/15980.html
这篇个人blog上也有相应分析:http://www.mingzhe.org/?tag=java-2
4.为何Collection不从Cloneable和Serializable接口继承?
Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。
个人理解:Collection extends 哪些接口就表示要具有哪些特征,extends的越多,限制要求就越多,而Collection只是extends了Iterable,表示Collection只需要具有可迭代的特性就可以了(Iterable接口中只定义了三个方法:hasNext(), next(), remove())
5.为何Map接口不继承Collection接口?
尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。
transient Set<Map.Entry<K,V>>entrySet;
public Set<K>keySet() { //抽取key的方法,返回Set(因为key不允许重复)
Set<K> ks;
return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
public Collection<V>values() { //抽取value的方法,返回Collection
(因为value允许重复)
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
public Set<Map.Entry<K,V>>entrySet() { //抽取key/value的方法,返回Set
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
PS: 由entrySet()得到Set<Map.Entry<K,V>>,可通过 迭代器 访问其中每一个元素,在Map接口中有定义Entry接口,Entry接口中又定义了getKey(),getValue(),setValue()等方法。
15.为何Iterator接口没有具体的实现?
Iterator 接口只是用于声明迭代集合的方法,但它的实现是依赖于具体的类的。 每个集合类都通过嵌套类的方式实现了自己的Iterator接口从而返回用于遍历的iterator对象。这样可以让集合类自己选择迭代器的实现是
fail-fast(每迭代一次时都检测集合结构是否发生变化,若变化则抛出CME异常)还是fail-safe(并发执行,不会抛出CME异常)机制。例如,ArrayList的迭代器就是fail-fast,而CopyOnWriteArrayList 的迭代器则是fail-safe的。
以ArrayList类源码中的iterator()方法和Itr内部类为例来分析:
15.HashMap的工作原理?
HashMap将键值对存储在实现 了Map.Entry接口的静态嵌套类中。HashMap是以哈希算法为基础的,在放入或取回元素时都会调用hashCode() 和equals()方法。当调用put()方法时,HashMap会调用“键”的hashCode()方法计算出”键值对”(Entry)将要存储在数组
中的位置(索引)。而Entry又是存储在LinkedList中,因此当计算出的位置已经有entry占用时,再调用equals()方法检查key是 否相同,如果相同那么用新值覆盖旧值,如果不相同,那么创建一个新的Entry,将键值对存入其中,再将Entry添加到LinkedList中。当调用 get()方法时,又会调用hashCode()方法来找出元素在数组中的位置,然后再调用equals()方法依次检查key是否相同,相同则返回 value。
对于HashMap,还要着重了解的是初始容量(capacity)、加载因子(load factor)、自动扩容(threshold resizing)机制。HashMap默认初始容量是32,加载因子是0.75。阈值(threshold)=初始容量x加载因子,当map的大小超过
阈值时,则要对该哈希表进行rehash操作,即重建内部数据结构(这一过程可能会引起性能问题),从而哈希表将具有大约两倍的容量,容量通常是2的幂次方。因此,如果能估测出元素的数量,那么最好在初始化HashMap时指定合适的capacity和
load factor。
这篇个人blog上也有相应分析:http://www.mingzhe.org/?tag=java-2
4.为何Collection不从Cloneable和Serializable接口继承?
Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。
个人理解:Collection extends 哪些接口就表示要具有哪些特征,extends的越多,限制要求就越多,而Collection只是extends了Iterable,表示Collection只需要具有可迭代的特性就可以了(Iterable接口中只定义了三个方法:hasNext(), next(), remove())
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable public abstract classAbstractList<E> extendsAbstractCollection<E> implementsList<E> public interfaceList<E> extendsCollection<E> public interface Collection<E> extends Iterable<E>
5.为何Map接口不继承Collection接口?
尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
transient Set<Map.Entry<K,V>>entrySet;
public Set<K>keySet() { //抽取key的方法,返回Set(因为key不允许重复)
Set<K> ks;
return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
public Collection<V>values() { //抽取value的方法,返回Collection
(因为value允许重复)
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
public Set<Map.Entry<K,V>>entrySet() { //抽取key/value的方法,返回Set
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
public abstract class AbstractMap<K,V> implements Map<K,V> public interface Map<K,V>
PS: 由entrySet()得到Set<Map.Entry<K,V>>,可通过 迭代器 访问其中每一个元素,在Map接口中有定义Entry接口,Entry接口中又定义了getKey(),getValue(),setValue()等方法。
15.为何Iterator接口没有具体的实现?
Iterator 接口只是用于声明迭代集合的方法,但它的实现是依赖于具体的类的。 每个集合类都通过嵌套类的方式实现了自己的Iterator接口从而返回用于遍历的iterator对象。这样可以让集合类自己选择迭代器的实现是
fail-fast(每迭代一次时都检测集合结构是否发生变化,若变化则抛出CME异常)还是fail-safe(并发执行,不会抛出CME异常)机制。例如,ArrayList的迭代器就是fail-fast,而CopyOnWriteArrayList 的迭代器则是fail-safe的。
以ArrayList类源码中的iterator()方法和Itr内部类为例来分析:
public Iterator<E> iterator() { return new Itr(); } /** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
15.HashMap的工作原理?
HashMap将键值对存储在实现 了Map.Entry接口的静态嵌套类中。HashMap是以哈希算法为基础的,在放入或取回元素时都会调用hashCode() 和equals()方法。当调用put()方法时,HashMap会调用“键”的hashCode()方法计算出”键值对”(Entry)将要存储在数组
中的位置(索引)。而Entry又是存储在LinkedList中,因此当计算出的位置已经有entry占用时,再调用equals()方法检查key是 否相同,如果相同那么用新值覆盖旧值,如果不相同,那么创建一个新的Entry,将键值对存入其中,再将Entry添加到LinkedList中。当调用 get()方法时,又会调用hashCode()方法来找出元素在数组中的位置,然后再调用equals()方法依次检查key是否相同,相同则返回 value。
对于HashMap,还要着重了解的是初始容量(capacity)、加载因子(load factor)、自动扩容(threshold resizing)机制。HashMap默认初始容量是32,加载因子是0.75。阈值(threshold)=初始容量x加载因子,当map的大小超过
阈值时,则要对该哈希表进行rehash操作,即重建内部数据结构(这一过程可能会引起性能问题),从而哈希表将具有大约两倍的容量,容量通常是2的幂次方。因此,如果能估测出元素的数量,那么最好在初始化HashMap时指定合适的capacity和
load factor。
相关文章推荐
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 转:40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 疯狂Java学习笔记(61)-----------40个Java集合面试问题和答案
- Java 40个集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个针对集合的Java面试问题和答案
- Java学习笔记(61)-----------40个Java集合面试问题和答案
- 40个Java集合面试问题和答案【上】【转载】
- 40个Java集合面试问题和答案【中】【转载】
- 40个Java集合面试问题和答案【下】【转载】
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案
- 40个Java集合面试问题和答案