您的位置:首页 > 职场人生

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())

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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: