ArrayList
# 前言
Java
集合可分为Collection
和Map
两种体系:
Collection
接口:单列数据,定义了存取一组对象的方法的集合List
:元素有序、可重复的集合ArrayList
、LinkedList
、Vector
Set
:元素无序、不可重复的集合HashSet
、LinkedHashSet
、TreeSet
Map
接口:双列数据,保存具有映射关系“key-value对
”的集合,也称为键值对。HashMap
、LinkedHashMap
、TreeMap
、Hashtable
、Properties
现在我们开始学习List接口的实现类ArrayList
。
# 概述
- ArrayList是List 接口的典型实现类、主要实现类
- 本质上,ArrayList是对象引用的一个”变长”数组,
Object[] elementData
,也就是动态的数组。
ArrayList的JDK1.8之前与之后的实现区别?
- JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
- JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
Arrays.asList(...)
方法返回的List 集合,既不是ArrayList实例,也不是Vector 实例。Arrays.asList(...)
返回值是一个固定长度的List 集合
# JDK7源码分析
在jdk7时,空参实例化方式:
ArrayList list = new ArrayList();
空参实例化时,底层是创建了长度是10的Object[] elementData
。
# 添加元素
添加元素时,在添加之前先确认容量是否够,不够就扩容,库容调用ensureCapacityInternal(size + 1)
方法,size
是原来的容量,调用grow
方法,默认情况下,容量扩容为原来的1.5倍,特殊情况会将容量设置为要添加元素的容量,极端情况,容量设置为整型最大值,如果容量还不够就报超出内存异常OutOfmemoryError()
。
扩容完以后,将原来数组中的元素拷贝过去。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int initialCapacity)
,直接设置容量空间,避免扩容,效率会更高。
# JKD8源码分析
先看空参构造器:
ArrayList list = new ArrayList();
空参实例化时,底层Object[] elementData
初始化为{}
,并没有创建长度为10的数组,
# 添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
2
3
4
5
添加元素时,第一次调用时,底层才创建了长度为10的数组,集合的索引指向这个元素,后续的添加和库容操作与JDK7无异。
# 小结
jdk7中的ArrayList的对象的创建类似于单例的饿汉式,实例化时先创建数组,有元素进来集合时就用索引指向元素。
而JKD8中的ArrayList的对象的创建类似于单例的懒汉式,实例化时不创建数组,延迟了数组的创建,节省内存。等有元素进来集合时才创建数组,并将索引指向元素。
# 面试题
下面输出结果是?
@Test
public void test1() {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
updateList(list);
System.out.println(list);
}
private static void updateList(List list) {
list.remove(2);
}
2
3
4
5
6
7
8
9
10
11
12
13
输出结果:
[1, 2]
这道题考的是按索引删除还是对象删除。
这里remove是调用Object remove(int index)
方法,而不是boolean remove(Object obj)
方法。
如果想要按Object删除,要传一个对象进去
list.remove(new Integer(2))