阿里面试常见题:ArrayListLinkedList和CopyOnWriteArrayList
Posted java-xiatian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里面试常见题:ArrayListLinkedList和CopyOnWriteArrayList相关的知识,希望对你有一定的参考价值。
ArrayList方面
ArrayList简介:
ArrayList是一种以数组实现的List,它实现了List, RandomAccess, Cloneable, Serializable接口。
- 实现List接口表示它可以支持删除、添加和查找等操作。
- 实现RandomAccess接口表示它可以支持随机访问(强调一点,并不是因为实现RandomAccess接口,ArrayList才支持随机访问。RandomAccess只是一个标记接口,接口RandomAccess中内容是空的,只是作为标记使用。)。
- 实现了Cloneable接口表示ArrayList可以被克隆。
- 实现了Serializable接口表示ArrayList可以被序列化。
ArrayList中的属性
- DEFAULT_CAPACITY =10;表示默认的容量为10。
- Object[] EMPTY_ELEMENTDATA ={};表示一个空数组,如果你传入的容量为0时使用,例如:ArrayList list=new ArrayList(0)。
- Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};表示一个空的数组,在你不传入容量的时候使用,例如:ArrayList list=new ArrayList()时使用。在添加第一个元素的时候会初始化默认的大小10.
- Object[] elementData;用于存储元素的数组。
- int size;表示元素的个数。
- modCount;对于add操作,remove操作,modCount会进行自增操作。表示ArrayList支持fail-fast。
ArrayList的构造方法(三种)
ArrayList list =new ArrayList(int initialCapacity)
传入初始容量,如果小于0就抛出异常;如果大于0,则使用传入的容量;如果等于0就使用EMPTY_ELEMENTDATA空数组。
ArrayList list=new ArrayList()
不传入初始容量,默认使用
DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组,在添加第一个元素的时候扩容为默认的大小DEFAULT_CAPACITY也就是10。
ArrayList list=new ArrayList(Collection c)
会将Collection集合转化为数组拷贝到element数组中(elementData=c.toArray())。
ArrayList的各种操作
add(E e)
将元素添加到末尾,平均时间复杂度为O(1);
- 检查数组是否需要扩容
- 首先判断elementData是否等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA即是否为空数组,如果是就初始化为默认大小DEFAULT_CAPACITY(10);
- 新容量为旧容量的1.5倍;如果发现还是比需要的容量小,则以需要的容量为标准;如果需要的容量已经超过最大的容量(MAX_ARRAY_SIZE),则使用最大的容量;最后以新容量拷贝出一个新的数组。
- 添加元素。
add(int index,E element)
将元素添加到指定位置,时间复杂度为O(n),因为需要将index后的元素整体向后移动一位。
- 检查index是否越界。
- 检查是否需要进行扩容。
- 将index后的元素整体向后移动一位。
- 将index位置的元素赋值为element。
- size++,表示元素个数增加一。
get(int index)
- 返回指定位置的元素,时间复杂度为o(1)。
- 判断index是否越界。
- 返回index位置的元素。
remove(int index)
删除指定位置的元素,时间复杂度为o(n),因为需要将index后的元素整体向前移动一位。
- 检查是否越界。
- modCount++。
- 调用System.arrayCopy()方法对于数组本身进行复制,将index后的元素整体向前移动一位。
- 将最后一个元素设置为null,帮助GC。
- 返回index位置的旧值。
remove(Object o)
删除指定元素的值,时间复杂为o(n)。
- 遍历数组,如果删除的元素为null,使用==进行null的比较;如果删除的元素不为null,使用equals()进行比较。
- modCount++;
- 调用System.arrayCopy()方法将删除元素位置后的所有元素整体向前移动一位。
- 将最后一个元素设置为null,方便进行GC。
- 返回true或false表示删除成功或者失败。
ArrayList总结
- ArrayList使用数组存储元素,当数组长度不够的时候,会进行扩容,每次扩容为1.5倍。
- ArrayList添加元素到尾部,平均时间复杂度为O(1)。
- ArrayList添加元素到中间,平均时间复杂度为O(n)。
- ArrayList删除尾部的元素,平均时间复杂度为O(1)。
- ArrayList从中间删除元素比价慢,平均时间复杂度为O(n)。
- ArrayList支持随机访问,时间复杂度为O(1)。
LinkedList相关方面
LinkedList简介:
相较于ArrayList的数组实现,LinkedList是基于双向链表实现的。它实现了List, Deque,Queue, Cloneable, Serializable接口。
- 实现List接口表示它可以支持删除、添加和查找等操作。
- 实现Deque和Queue接口表示它可以作为双端队列进行使用,同时也可以作为栈进行使用。
- 实现了Cloneable接口表示ArrayList可以被克隆。
- 实现了Serializable接口表示ArrayList可以被序列化。
LinkedList的内部属性
int size表示元素的个数,初始为0。
- Node< E> first。表示链表的第一个节点。
- Node< E> last。表示链表中的最后一个节点。
- Node< E> 是一个双向链表的结构。Node< E>内部包括Node< E> next,Node< E> prev和E item。
- modcount表示支持fail-fast。
LinkedList的主要方法
addFirst(E e)
在队首添加元素,时间复杂度为O(1)。
- 创建一个新的节点newNode。
- 将newNode赋值给first即first=newNode。
- 判断newNode是不是第一个新添加的节点,如果是的话,将newNode赋值给last即last=newNode。
- size++。
- modcount++。
addLast(E e)
在队尾添加元素和addFirst(E e)同理,时间复杂度为O(1)。
add(int index,E element)
在指定位置添加元素,时间复杂度为O(n)。因为需要遍历,查找到插入位置的后继节点。
- 判断是否越界。
- 如果index小于size/2,从前往后进行遍历找到插入位置的后继节点;如果index大于size/2,从后往前遍历找到插入位置的后继节点。
- 按照双线链表在中间插入元素的方法进行插入。
removeFirst()
删除队首的元素,时间复杂度为O(1)。
- 如果链表中没有元素,抛出异常。
- 查找first的后继节点假设是first_next,将first设置为null,方便进行GC。
- 将first设置为first_next即first=first_next;判断first_next是否为null,如果first_next等于null,则将last设置为null否则将first_next.prev设置为null。
- size–。
- modcount++。
removeLast()
删除队尾的元素,时间复杂度为O(1)。原理同上。
remove(int index)
删除指定位置的元素,时间复杂度为O(n),原理同add(int index,E element)。
LinkedList总结
- LinkedList是基于双向链表实现,可以作为双端队列,栈来进行使用。
- LinkedList在队首队尾删除添加比较快,时间复杂度为O(1)。
- LinkedList在链表中间添加删除比较慢,时间复杂度为O(n)。
- 另外要注意的是,LinkedList是不支持随机访问的,所以访问中间元素的效率比较低。
CopyOnWriteArrayList相关
CopyOnWriteArrayList简介:
CopyOnWriteArrayList是ArrayList的线程安全版本,也是通过数组实现的。相较于ArrayList,CopyOnWriteArrayList使用ReentrantLock重入锁加锁,保证线程安全。每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不阻塞读操作,实现读写分离。
CopyOnWriteArrayList实现了List, RandomAccess, Cloneable, Serializable接口。
- 实现List接口表示它可以支持删除、添加和查找等操作。
- 实现RandomAccess接口表示它可以支持随机访问(强调一点,并不是因为实现了RandomAccess接口,ArrayList才支持随机访问。RandomAccess只是一个标记接口,接口RandomAccess中内容是空的,只是作为标记使用。)。
- 实现了Cloneable接口表示ArrayList可以被克隆。
- 实现了Serializable接口表示ArrayList可以被序列化。
CopyOnWriteArrayList主要的内部属性:
- ReentrantLock lock=new ReentrantLock();用于修改时进行加锁。
- Object[]array用于纯属元素。
CopyOnWriteArrayList主要方法:
add(E e)
添加一个元素到末尾。
- 使用ReentrantLock进行加锁。
- 使用getArray()方法获取元素的数组。
- 新建一个数组大小是原数组的长度加一,并使用Arrays.copyof()方法将原数组拷贝到新数组中。
- 将添加的元素放到新数组的末尾。
- 将新数组覆盖原数组。
- 解锁。
add(int index,E element)
添加元素到制定的位置。
- 加锁。
- 判断是否越界,如果越界抛出异常。
- 如果索引等于数组长度,那就拷贝一个len+1的数组即newElements =Arrays.copyOf(elements,len+1)。
- 如果索引不等于数组长度,那就新建一个len+1的数组,将索引之前的部分拷贝到新数组索引之前,索引之后拷贝到新数组索引之后的位置。
- 把索引位置赋值为待添加的元素。
- 把新数组赋值给当前对象的array属性,覆盖原数组。
- 解锁。
get(int index)
获取指定位置的元素,时间复杂度为O(1)。
- 通过getAway()方法获取数组。
- 返回指定位置的元素。
remove(int index)
删除指定位置的元素,时间复杂度O(n)。
- 加锁。
- 通过getAway()方法获取旧数组。
- 如果移除的是最后一位,那么直接拷贝一份n-1的新数组, 最后一位就自动删除。
- 如果移除的不是最后一位,那么新建一个n-1的新数组。将前index的元素拷贝到新数组中,将index后面的元素往前挪一位拷贝到新数组中。
- 将新数组赋值给当前对象的数组属性。
- 解锁并返回旧值。
CopyOnWriteArrayList总结:
- 对于写操作,CopyOnWriteArrayList使用ReentrantLock重入锁加锁,保证线程安全。
- CopyOnWriteArrayList的写操作要先拷贝一份新数组,在新数组中进行修改,修改完了再用新数组去替换旧的数组。
- CopyOnWriteArrayList采用读写分离的思想,读操作不加锁,但是写操作需要加锁。
- CopyOnWriteArrayList支持随机快速访问,时间复杂度为O(1)。
以上是关于阿里面试常见题:ArrayListLinkedList和CopyOnWriteArrayList的主要内容,如果未能解决你的问题,请参考以下文章
200道带答案的常见Java面试题!据说某阿里P7只答上了70%!
列举出常见的Java面试题,我靠这个在春招拿到了阿里的offer