Java集合部分面试题

85 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

Collection下的集合

List 集合:

ArrayList()有序的动态数组列表,只能装载包装类(Integer、String、Double、Object等),允许为null重复元素,特点:查询快、增加、删除慢

Arrraylist扩容机制

  • 使用ArrayList()构造函数时,会构造一个长度为0的数组,是一个Object 数组.
  • ArrayList(int initalCapacity)会使用指定容量的数组
  • public ArrayList(Collection <? extends E> c) 会使用集合c的大小作为数组容量
  • add(Object o)首次扩容为10,再次扩容为上次容量的1.5倍(0.5 采用的是右移运算)且只针对初始容量为0的数组,每次扩容1.5倍的大小依次为: [0、10、15、22、33、49、73、109、163、244、366、549、823、1234、1851、2776]
  • addAll(Collection c) 没有元素时,扩容为Math.max(10,实际元素的个数),有元素时为Math.max(原始容量的1.5倍,实际元素的个数)

fail-fastfail-safe

  • ArrayListfail-fast的典型代表,遍历的同时不能修改,会报异常,尽快失败
  • CopyOnWriteArrayListfail-safe的典型代表,遍历时可以修改,原理是读写分离

LinkedListArrayList区别:

  • ArrayList底层基于动态数组实现的,LinkedList底层基于双向链表实现的

  • 访问:对于随机访问,(get/set访问)ArrayList实现了 **RandomAccess**接口,可以直接定位到数组对应下标节点,而LinkedList需要从头节点或者尾节点开始遍历直到找到目标元素,所以:Arraylist访问速率 > LinkedList访问速率

  • 插入和删除

    • 随机插入和删除:ArrayList需要移动目标后面的节点(使用System.arrarcopy移动节点),而LinkedList只需要修改目标节点的next或者prev,所以:ArrayList随机插入删除效率 < LinkedList随机插入和删除效率
    • 顺序插入和删除:ArrayList不需要移动节点,ArrayLists顺序插入效率>LinkedList顺序插入和删除,大多数情况使用ArrayList,因为大多数情况都是顺序插入
    • 头尾插入删除:ArrayList尾部插入、删除性能,但是部分插入需要移动元素,性能低。LinkedList头部尾部插入删除性能
  • 线程安全:LinkedList和ArrayList线程均不安全

Map下的集合

HashMap的面试题

  • JDK1.7:HashMap底层为数组+链表
  • JDK1.8:HashMap底层为数组+链表+红黑树

为何用红黑树,何时为转化为红黑树,为什么树化的阈值为8?

  • 红黑树用来避免Dos攻击,防止链表超长,性能下降
  • 当链表长度>8,并且数组容量>=64,会转化为红黑树
  • hash采用泊松分布,在负载因子0.75的情况下,长度超过8,链表出现的概率最小,选择8是为了让树化几率足够小

索引如何计算?HashCode有了,为什么要二次哈希?数组容量为什么为2的n次幂?

  • 先计算对象的hashcode(),在调用HashMap的hash()方法进行二次哈希,最后&(capacity-1)得到索引
  • 二次哈希是为了让哈希分布更均匀
  • 计算索引时,如果是2的n次幂可以使用按位与运算代替取模运算,效率更高

HashMapHashTable不同

  • 版本不同:Hashtable出生于JDK1.0,HashMap出生于JDK1.2
  • 继承不同:HashMap继承AbstractMap(AbstractMap实现了Map接口),Hashtable继承Dictonary
  • 线程安全:Hashtable大部分普通方法都是synchronized修饰的,是线程安全,HashMap实现从不安全的
  • Key---Value:Hashtable的key、value都不能为null,源码:
public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }
​
        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode(); //此时key为空也会报NullPointerException()
  • Key---Value:HashMap的Key、Value都可以为空,计算Hash有判断,如果Key==null,则hash==0,且Value是否为不做判断
 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
  • 初始容量不同:Hashtable 的初始长度是 11,之后每次扩充容量变为之前的 2n+1(n 为上一次的长度)而 HashMap 的初始长度为 16,之后每次扩充变为原来的两倍