集合框架面试题总结-1

  • ArrayList的扩容机制
    以无参构造方法创建ArrayList时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。

      /**
         * Default initial capacity.
         */
        private static final int DEFAULT_CAPACITY = 10;
    
        /**
         * Shared empty array instance used for empty instances.
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
    

    因为在源代码中,int newCapacity = oldCapacity + (oldCapacity >> 1),所以ArrayList每次扩容之后容量都会变为原来的1.5倍。

      private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

如果minCapacity大于大容量,则新容量为Integer.MAX_VALUE,否则新容量大小为MAX_ARRAY_SIZE,即为Integer.MAX_VALUE - 8。

  private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
  • ArrayList和Vector的区别是什么?为什么要用ArrayList取代Vector?

    • Vector类的索引方法都是同步的。可以有两个线程安全地访问一个Vector对象,但是一个线程访问Vector的话代码要在同步操作上耗费大量时间。
    • ArrayList不是同步的,所以不需要保证线程安全时建议使用ArrayList
  • ArrayList与LinkedList的区别?

    1. 是否保证线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全
    2. 底层数据结构:ArrayList底层使用的是Object数组;LinkedList底层使用的是双向链表数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到)
    3. 插入和删除是否首元素位置的影响:① ArrayList采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。比如:执行add(E e)方法的时候,ArrayList会默认在指定的元素追加到此列表的末尾,这种情况的时间复杂度就是O(1)。但是如果要在指定位置i插入和删除元素的话(add ( int index, E element))时间复杂度就是O(n-i)。因为在进行上述操作的时候集合中第i和第i个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。② LinkedList采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似O(1),如果是要在指定位置i插入和删除元素的话,(add ( int index, E element))时间复杂度近似为O(n),因为需要先移动到指定位置再插入。
    4. 是否支持快速随机访问:LinkedList不支持高效的随机元素访问,而ArrayList支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index) 方法)。
    5. 内存空间占用:ArrayList的空间浪费主要体现在 在list列表的结尾会预留一定的容量空间,而LinkedList的容量空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
  • HashMap与Hashtable的区别

    1. 线程是否安全:HashMap是非线程安全的,Hashtable是线程安全的;Hashtable内部的方法基本都经过synchronized修饰。(如果一定要保证线程安全的话可以使用ConcurrentHash);
    2. 效率:因为线程安全的问题,HashMap要比Hashtable效率高一点。另外,Hashtable基本被淘汰,不要在代码中使用它;
    3. 对 Null key 和 Null value 的支持:HashMap中,null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null。但是在Hashtable中put进的键值只要有一个null,就直接抛出 NullPointerException。
    4. 初始容量大小和每次扩容量大小的不同: ① 创建时如果不指定容量初始值,Hashtable默认的初始大小为11,之后每次扩容,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。② 创建时如果给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说HashMap总是使用2的幂次方作为哈希表的大小。
    5. 底层数据结构:JDK1.8以后的HashMap在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转换为红黑树,以减少搜索时间。Hashtable没有这样的机制。
  • List、Set、Map三者的区别

    • List(有序、重复):List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
    • Set(无序、不重复):不允许重复的集合。不会有多个元素引用相同的对象。
    • Map(KV键值对集合):使用键值对来存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象。但Key不能重复,典型的Key是String类型,但也可以是任何对象。
  • RandomAccess接口

    public interface RandomAccess {
    }
    

    通过查看源码可以发现实际上RandomAccess接口中什么都没有定义。所以我认为RandomAccess接口只是一个标识,标识实现这个接口的类具有随机访问功能。

    在binarySearch( ) 方法中,它要判断传入的list是否为RandomAccess接口的实现类的实例,如果是,调用indexBinarySearch( )方法,如果不是,则调用iteratorBinarySearch( )方法。

    public static <T>
        int binarySearch(List<? extends Comparable<? super T>> list, T key) {
            if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
                return Collections.indexedBinarySearch(list, key);
            else
                return Collections.iteratorBinarySearch(list, key);
    }
    

    ArrayList实现了RandomAccess接口,而LinkedList没有实现。是什么原因?

    ​ 我认为还是与底层数据结构有关。ArrayList底层是数组,而LinkedList底层是链表。数组天然支持随机访问,时间复杂度为O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问到特定位置的元素,时间复杂度为O(n),所以不支持快速随机访问。ArrayList实现了RandomAccess接口,就表明了他具有快速随机访问功能。RandomAccess接口只是标识,并不是说ArrayList实现RandomAccess接口才具有快速随机访问功能的!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容