小言_互联网的博客

聊聊ArrayList的问题

425人阅读  评论(0)

List这种数据结构可以说在我们的实际工作中经常会使用到,最常见的就是ArrayList和LinkedList两种实现类了,两种结构的操作看似相似,但是原理却差别巨大。关于这两者的差别,小编特意总结了一些干货和各位读者们进行分享。

ArrayList和LinkedList的区别?

通过阅读util包里面的源码可以很容易的看出两者的大致区别:

ArrayList是一种具有动态扩容特性且基于数组基础的数据结构,而LinkedList则是一种基于链表的数据结构。

在进行元素查找的时候适合用ArrayList进行操作,在进行元素的添加和删除的时候适合用LinkedList。由于ArrayList是采用数组作为数据结构的,因此在进行查找的时候只需要根据下标的索引进行判断即可。

LinkedList数据结构则是采用链表的结构进行设计的,因此在查找的时候需要进行逐一比较,所以效率会比较慢(并非是全链查询,而是采用了折半搜索的方式来进行优化)。在添加或者删除元素的时候,由于ArrayList需要进行元素的位置移动,而链表的移动和删除只需要将链表节点的头尾指针进行修改即可。

ArrayList的动态扩容有什么特点?

当我们在进行ArrayList的插入元素时候,相应的元素会被插入到动态数组里面,但是由于数组本身所能存储的数据量是有限制的,因此在插入数据的时候,需要进行相应的动态扩容,在看源码的时候,可以看到相应的代码部分:

add操作源码:

1   /**
 2     * Appends the specified element to the end of this list.
 3     *
 4     * @param e element to be appended to this list
 5     * @return <tt>true</tt> (as specified by {@link Collection#add})
 6     */
 7    public boolean add(E e) {
 8        ensureCapacityInternal(size + 1);  // Increments modCount!!
 9        elementData[size++] = e;
10        return true;
11    }

核心扩容部分的实现:

 1     private void ensureCapacityInternal(int minCapacity) {
 2        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
 3            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
 4        }
 5
 6        ensureExplicitCapacity(minCapacity);
 7    }
 8
 9    private void ensureExplicitCapacity(int minCapacity) {
10        modCount++;
11
12        // overflow-conscious code
13        if (minCapacity - elementData.length > 0)
14            grow(minCapacity);
15    }

ArrayList默认数组的大小为10,扩容的时候采用的是采用移位运算

11int newCapacity = oldCapacity + (oldCapacity >> 1); 

这里也可以看出ArrayList的扩容因子为1.5。(4>>1 就相当于4除以2 即缩小了一半)。

什么时候会选择使用ArrayList

这又是一个大多数面试者都会困惑的问题。多数情况下,当遇到访问元素比插入或者是删除元素更加频繁的时候,应该使用ArrayList。另外一方面,当在某个特别的索引中,插入或者是删除元素更加频繁,或者压根就不需要访问元素的时候,不妨考虑选择LinkedList。

这里的主要原因是,在ArrayList中访问元素的最糟糕的时间复杂度是O(1),而在LinkedList中可能就是O(n)了。在ArrayList中增加或者删除某个元素时候,如果触发到了扩容机制,那么底层就会调用到System.arraycopy方法,如果有兴趣深入挖掘jdk源码的话,会发现这是一个本地调用方法,被native修饰,该方法会直接通过内存复制,省去了大量的数组寻址访问等时间,但是相比于LinkedList而言,在频繁的修改元素的情况下,选用LinkedList的性能会更加好一点。

 1  public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
 2        @SuppressWarnings("unchecked")
 3        T[] copy = ((Object)newType == (Object)Object[].class)
 4            ? (T[]) new Object[newLength]
 5            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
 6        //复制集合
 7        System.arraycopy(original, 0, copy, 0,
 8                         Math.min(original.length, newLength));
 9        return copy;
10    }

如果读者有去学习过jvm的话,应该会对“内存碎片“这个名词比较熟悉。基于数组结构的数据在存储信息的时候都需要有连续的内存空间,所以如果当内存碎片化情况较为严重的时候,可能在使用ArrayList的时候会有OOM的异常抛出。

如何复制某个ArrayList到另一个ArrayList中去?

1.使用clone()方法,比如ArrayList newArray = oldArray.clone();

2.使用ArrayList构造方法,比如:ArrayList myObject = new ArrayList(myTempObject);

3.使用Collection的copy方法。

关于list集合的拷贝问题在面试中可能还会引申出深拷贝和浅拷贝的对比,小编后期会去专门写一篇笔记做讲解。

请说说ArrayList、Vector和LinkedList的区别 

相同点

这三者都是单列集合Collection下List集合的实现类,所以他们的共同点,元素有序,允许重复元素 。

不同点

Collection和Collections的区别 

Collection是单列集合的父接口,翻译过来则是指容器的意思,常见的Set接口,List接口都是属于Collection接口下边的内容。

Collections是工具类,提供了关于集合的常用操作,例如,排序、二分法查找、反转元素等。

常用的一些Collections的api:

1   Collections.sort(list);
2   Collections.emptyList();
3   Collections.binarySearch(list,"your key");
4   Collections.copy(list,list1);
5   System.arraycopy(list,1,list1,2,1);

其实java里面已经默认提供好了很多有用的工具类给开发者们,我们除了要熟悉里面的api使用之外,还需要了解各种api的使用策略以及不同场景下的使用优势。

modCount参数的意义

在ArrayList设计的时候,其实还包含有了一个modCount参数,这个参数需要和expectedModCount 参数一起使用,expectedModCount参数在进行修改的时候会被modCount进行赋值操作,当多个线程同时对该集合中的某个元素进行修改之前都会进行expectedModCount modCount的比较操作,只有当二者相同的时候才会进行修改,两者不同的时候则会抛出异常。这个机制也被称之为fail-fast机制。

COW容器

jdk1.5之前,由于常用的ArrayList并不具有线程安全的特性,因此在1.5之后的并发包里面出现了CopyOnWrite容器,简称为COW。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

并发包juc里面的CopyOnWriteArrayList中,核心原理主要是通过加入了ReentrantLock来保证线程安全性,从而解决了ArrayList的线程安全隐患问题。

相应的add操作源码如下


 

如果读者对于ArrayList的具体实现充满好奇心的话,不妨可以试着自己边看源码边动手写一套相应的数据结构,相信坚持下来一定会大有收获。


转载:https://blog.csdn.net/weixin_38405253/article/details/100589077
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场