集合类型之ArrayList的特点,面试学习工作必备!!!

ArrayList的扩容机制

版本区别: jdk1.7之前就是初始化一个初始长度为10的数组, 1.8 就是初始化一个初始长度为0的数组.

初始容量的选择:ArrayList()无参构造会使用长度为零的数组

​ ArrayList(int initCapacity)带参构造会使用长度为指定参数的数组

​ public ArrayList(Collection<? extends E> c) 会使用容器c的大小作为数组长度

add(Object o) 方法首次扩容为10,再次扩容为上次容量的1.5倍

1.	首次容量为0;
2.	调用add方法,加入第一个元素时数组扩容为10.
3.	继续调用add方法,当加入的元素超过10时,重新定义一个原数组1.5倍长的数组, 调用System.copy本地方法 , 将原数组中的数据拷贝到新数组.
//不是  扩容后=扩容前*1.5
//而是  扩容后=扩容前+扩容前>>1 ,采用移位运算,右移一位,相当于除以2,防止出现小数
4.  把ArrayList的指针指向新数组.

addAll(Collection c)没有元素时,扩容为Math.max(10,实际元素个数),有元素时为Math.max(原容量的1.5倍,实际元素个数)

//原始数组已经有10个元素
//使用addAll(List.of(1,2,3)),扩容为15         原因:Math.MAX(下次扩容的长度:15,实际长度:13)
//使用addAll(List.of(1,2,3,4,5,6)),扩容为16   原因:Math.MAX(下次扩容的长度:15,实际长度:16)

Iterator的FailFast和FailSafe

FailFast:

一旦发现遍历的同时其他人来修改,则立即抛出异常------>ArrayList

FailSafe:

遍历的同时其他人来修改,则牺牲一致性来保证遍历的完成,即只能遍历出修改之前的数组元素---->CopyOnWriteArrayList

ArrayList和LinkedList的比较

// ArrayList:
// 基于数组,需要连续内存
// 随机访问快(需根据下标访问)
// 尾部插入与删除性能可以,其他部分插入删除都会移动数据,性能低,其他部位插入是直接复制一份新的数组,将待插入元素索引后面的元素后移
// 可以利用CPU缓存,局部性原理

// LinkedList:
// 基于双向链表,无需连续存储
// 随机访问慢(因为链表的特性,每个节点只存储相邻节点的数据,所有需要从第一个元素开始沿着链表查找)
// 头尾插入和删除性能都高
// 占用内存多

// 两着都是线程不安全的, 需要线程安全的情况下需要使用Vector或者CopyOnWriteList
关于查询:

如果都是根据下标查找,那么ArrayList的查询效率高于LinkedList, 因为链表嘛, 不像redis底层的跳表一样, 可以使用多级节点查找

如果根据查找的内容来查询,他们两个效率是一样的,时间复杂度都是O(n),应该使用HashMap或者TreeMap这样的数据结构来实现根据内容查询

关于插入

头部插入,LinkedList速度快于ArrayList,因为ArrayList要复制一份新的数组,新插入元素在索引0,其他原来的元素索引后移一位

尾部插入,ArrayList的速度略快与LinkedList,ArrayList不涉及数组的复制移动,索引一定位直接插入

中间插入,ArrayList的速度远高于LinkedList,因为LinkedList要调用next()方法遍历查找要插入的位置,查找到之后才能进行插入。

关于内存的局部性原理

读取数组某个元素时,因为数组是连续的,CPU会假设读取时会访问到相邻的其他元素,CPU缓存会一并读取此元素相邻的其他元素,

读取链表的某个元素时,因为链表是不连续的,CPU缓存读取某节点元素时会读取到与次节点不相邻的元素,造成缓存空间的浪费,导致下次读取很可能覆盖掉之前已经缓存的数据

关于占用内存

LinkedList底层是由一个个Node对象组成的,每个Node对象由元素,上一个指针,下一个指针组成,占用内存很大

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;

}
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;java
        this.next = next;
        this.prev = prev;
   }
}

如何复制某个ArrayList到另一个ArrayList中去?写出你的代码?

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

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

使用Collections的copy方法。java

clone()和构造方法都是浅拷贝,
Collections.copy(List target, List src)是深拷贝, 拷贝后的数组长度必须大于或者等于源数组.

Collection.copy()注意事项
        ArrayList<Integer> arrayList=new ArrayList<Integer>(5);
        arrayList.add(1);java
        arrayList.add(2);

        // 直接在构造方法里面声明长度并不会初始化长度,仍然为0,只是声明的数组容量,而不是实际长度
        // ArrayList<Integer> list=new ArrayList<>(5);

        // Arrays.asList(new Integer[arrayList.size()]) 会初始化全为null的一个数组,长度为arrayList的长度,再转为list
        List<Integer> list = Arrays.asList(new Integer[arrayList.size()]);
        Collections.copy(list,arrayList);

浅拷贝与深拷贝

基本类型 :

整型: byte, short, int ,long
浮点型: float,double
字符型: char
布尔型: boolean

引用数据类型:

类,接口,数组
  • 浅拷贝就是创建一个新对象, 这个对象有着原始对象属性值的一份精确拷贝. 如果属性是基本类型, 拷贝的就是基本类型的值, 如果属性是引用类型, 拷贝的就是内存地址.
  • 深拷贝就是将一个对象从内存中完整的拷贝一份出来, 从堆内存中开辟了一个新的区域存放新对象.
  • 浅拷贝之后虽然两个对象的引用不同了,但指向的还是同一片内存空间, 一旦其中一个改变了内存中的数据, 另一个也会随之改变.
  • 深拷贝之后, 新对象与就对象完全是两个不同的对象, 每个对象各自使用自己的内存空间,互不影响.

怎么确保一个集合不能被修改?

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出

Java. lang. UnsupportedOperationException 异常

ArrayList频繁扩容导致添加性能急剧下降,如何处理?

使用ArrayList时,可以 new ArrayList(大小)构造方法来指定集合的大小,以减少扩容的次数,提高写入效率。

ArrayList线程安全吗

不安全, ArrayList和LinkedList都是不安全的.

当开启多个线程操作List集合,向ArrayList中增加元素,同时去除元素,可能导致有些元素为null, 或者导致数组下标越界异常

  • 一般使用Vector这个线程安全的数组容器,它给所有方法都加上了锁

  • 或者使用Collections.synchronizedList将ArrayList包装成线程安全的数组容器

  •     Vector vector=new Vector();
        List<Object> objects = Collections.synchronizedList(new ArrayList<>());
    

为什么ArrayList线程不安全,我们还使用它?

因为大多数的场景中,查询操作使用频率高,增删操作的使用频率低。 如果涉及频繁的增删,可以使用LinkedList,实际开发过程中还是ArrayList使用最多的。不存在一个集合既查询效率高,又增删效率高,还线程安全的,因为数据结构的特性就是优劣共存的,想找个平衡点很难,牺牲了性能,那就安全,牺牲了安全那就快速。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木 木 水.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值