1、ArrayList是什么?可以用来干嘛?
ArrayList就是有序的动态数组列表,主要⽤来装载数据,只能装载包装类(Integer,String,Double等),它的主要底层实现是数组Object[]
elementData
2、ArrayList与LinkedList的区别?
1、ArrayList的查找和访问元素的速度较快,但新增,删除的速度较慢,LinkedList的查找和访问元素的速度较慢,但是他的新增,删除的速度较快
2、ArrayList需要一份连续的内存空间,LinkedList不需要连续的内存空间(特别地,当创建一个ArrayList集合的时候,连续的内存空间必须要大于等于创建的容量)
3、两者都是线程不安全的
3、为啥线程不安全还使用他呢?
因为在我们正常得使用场景中,都是用来查询的,不会涉及太频繁的增删,如果涉及到频繁的增删,可以使用LinkedList,如果需要线程安全的就是可以使用Vector,CopyOrWriteArrayList
4、它的底层实现是数组,但是数组的⼤⼩是定⻓的,如果我们不断的往⾥⾯添加数据的话,不会有问题吗?
JDK1.7–》相当于设计模式中的饿汉式 第一次创建无参构造器时就创建一个初始容量为10的数组 JDK1.8
–》相当于涉及模式中的懒汉式 ArrayList可以通过构造⽅法在初始化的时候指定底层数组的⼤⼩。 通过⽆参构造⽅法的⽅式ArrayList()初始化,则赋值底层数Object[] elementData为⼀个默认空数组 Object[]
DEFAULTCAPACITY_EMPTY_ELEMENTDATA =
{}所以数组容量为0,只有真正对数据进⾏添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。 private
static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//无参数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//有参数
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
5、ArrayList数组扩容
假设我们有一个长度为10的数组,此时需要新增一个,发现满了,ArrayList会进行扩容
1、重新定义一个10*1.5的数组
然后把原数组的数据,原封不动的复制到新数组中,然后把ArrauList的地址指向新数组
//详细扩容
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);
}
6、1.7和1.8版本初始化的区别
1.7的时候是初始化就创建一个容量为10的数组,1.8后是初始化先创建一个空数组,第一次add时才扩容为10
7、ArrayList在增删的时候是怎么做的?(为什么慢)
新增
ArrayList有指定index(索引下标)新增,也有尾部新增,但是都有校验长度的判断ensureCapacityInternal,就是说如果⻓度不够,是需要扩容的。
在扩容的时候,1.7是取余,1.8是位运算,右移⼀位,其实就是除以2这个操作。
//尾插add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//index(索引)插入,指定位置新增的时候,在校验之后的操作很简单,就是数组的copy
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//真正扩容
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);
}
//删除
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
总结:总的来说,不论是删除还是新增,其实本质上都是移动位置,当指定位置新增的时候,新增的索空出,后面向后移一位,然后赋值,当删除时,也是一样,将要删除的索引下标的值置为null。
8、ArrayList(int initialCapacity)会不会初始化数组⼤⼩?
会初始化大小,但是list的大爱小没有变,也就是size不变
⽽且将构造函数与initialCapacity结合使⽤,然后使⽤set()会抛出异常,尽管该数组已创建,但是⼤⼩设置不正确。
使⽤sureCapacity()也不起作⽤,因为它基于elementData数组⽽不是⼤⼩。
还有其他副作⽤,这是因为带有sureCapacity()的静态DEFAULT_CAPACITY。
进⾏此⼯作的唯⼀⽅法是在使⽤构造函数后,根据需要使⽤add()多次。
9、ArrayList是线程安全的么?
不是,可以用Vector,Collections.synchronizedList(),原理都是給方法套个synchronized,
CopyOnWriteArrayList
10、ArrayList⽤来做队列合适么?
队列⼀般是FIFO(先⼊先出)的,如果⽤ArrayList做队列,就需要在数组尾部追加数据,数组头部删除
数组,反过来也可以。但是⽆论如何总会有⼀个操作会涉及到数组的数据搬迁,这个是⽐较耗费性能的。
结论:ArrayList不适合做队列。
11、那数组适合⽤来做队列么?
数组是⾮常合适的。 ⽐如ArrayBlockingQueue内部实现就是⼀个环形队列,它是⼀个定⻓队列,内部是⽤⼀个定⻓数组来实现的。
另外著名的Disruptor开源Library也是⽤环形数组来实现的超⾼性能队列,具体原理不做解释,⽐较复杂。
简单点说就是使⽤两个偏移量来标记数组的读位置和写位置,如果超过⻓度就折回到数组开头,前提是它们是定⻓数组。
12、ArrayList的遍历和LinkedList遍历性能⽐较如何?
ArrayList要比LinkedList快得多,ArrayList遍历最大的优势在于内存的连续性,CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销。
13、ArrayList常⽤的⽅法总结
boolean add(E e)
将指定的元素添加到此列表的尾部。 void add(int index, E element)
将指定的元素插⼊此列表中的指定位置。 boolean addAll(Collection<? extends E> c)
按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。 boolean addAll(int index, Collection<? extends E> c)
从指定的位置开始,将指定 collection 中的所有元素插⼊到此列表中。 void clear()
移除此列表中的所有元素。 Object clone()
返回此 ArrayList 实例的浅表副本。 boolean contains(Object o)
如果此列表中包含指定的元素,则返回 true。 void ensureCapacity(int minCapacity)
如有必要,增加此 ArrayList 实例的容量,以确保它⾄少能够容纳最⼩容量参数所指定的元素数。 E get(int index)
返回此列表中指定位置上的元素。 int indexOf(Object o)
返回此列表中⾸次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。 boolean isEmpty()
如果此列表中没有元素,则返回 true int lastIndexOf(Object o)
返回此列表中最后⼀次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。 E remove(int index)
移除此列表中指定位置上的元素。 boolean remove(Object o)
移除此列表中⾸次出现的指定元素(如果存在)。 protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。 E set(int index, E element)
⽤指定的元素替代此列表中指定位置上的元素。 int size()
返回此列表中的元素数。 Object[] toArray()
按适当顺序(从第⼀个到最后⼀个元素)返回包含此列表中所有元素的数组。 T[] toArray(T[] a)
按适当顺序(从第⼀个到最后⼀个元素)返回包含此列表中所有元素的数组;返回数组的运⾏时类型是指定数组的运⾏时类型。 void trimToSize()
将此 ArrayList 实例的容量调整为列表的当前⼤⼩。
声明:本文是参考敖丙大佬的推文下写的,若有侵权,请私聊我
我是小白弟弟,一个在互联网行业的小白,立志成为一名架构师
https://blog.csdn.net/zhouhengzhe?t=1