Javaee - 11集合Collection-List
(4)Collection子接口:List接口
(4.1)List接口概述
- 鉴于数组存储数据的局限性,通常使用List替代数组。List-> "动态"数组。
- List集合类中元素有序、可重复,集合中的每个元素都有其对应的顺序索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的实现类有: ArrayList、 LinkedList 和 Vector。三者异同。
- ArrayList: List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
- LinkedList:对于频繁插入删除操作,效率比ArrayList高;底层使用双向列表存储
- Vector: List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储
(4.2)ArrayList
(4.2.1)JDK 7下源码分析
- ArrayList list = new ArrayList(); //底层创建初始长度为10的Object[]数组elementData
- list.add(123); // elementData[0] = new Integer(123);
- ......
- list.add(11); // 如果此次添加导致底层elementData数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时将原有数组中的数据复制到新的数组中。
- 建议: 开发中使用带参数的构造器: ArrayList list = new ArrayList(int capacity); 节省扩容的性能开支
(4.2.2)JDK 8中的变化
- ArrayList list = new ArrayList(); //底层Object[]数组elementData初始化为{},不创建长度10的数组
- list.add(123); // 第一次调用add时,底层创建长度10的数组,添加元素elementData[0] = new Integer(123);
- 后续的添加和扩容操作与JDK7一样。
- JDK7对象的创建类似于单例的饿汉式,JDK8的ArrayList对象创建类似于单例的懒汉式,延迟了数组创建,节省内存。
(4.2.3)重要源码(JDK8)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { // 不带参数,初始化为空数组 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
private static final Object[] 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); } }
public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 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) // MAX_ARRAY_SIZE = Integer.MAX_VALUE -8 newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
(4.3)LinkedList
- linkedList 是一个双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。
- 定义内部类Node,作为LinkedList中保存数据的基本结构。
- Node除了保存数据,还定义了两个变量: prev变量记录前一个元素的位置,next变量记录下一个元素的位置。
- 没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增Node。
对于双向链表Node的理解
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; this.next = next; this.prev = prev; } }
在列表尾部插入一个元素
- 先将列表最后一个元素last 保存为Node节点 l;
- 组装一个新的Node节点newNode, Node 的 prev 是 l, 值为e, next 为null。
- 将新的节点newNode 作为列表的最后一个元素。
- 将原来元素的 next 指向 newNode新节点。 如果是第一个元素,则把 first 也指向新节点。
- 列表 size 加1, modCount 加1。
void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; } void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
(4.4)Vector
JDK7 和 JDK8 中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍。
public Vector() { this(10); } public synchronized void ensureCapacity(int minCapacity) { if (minCapacity > 0) { modCount++; ensureCapacityHelper(minCapacity); } } private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
(4.5)List接口
除了继承Collection的方法,List添加了一些根据索引来操作集合元素的方法。
- void add(int index, Object ele): 在index位置插入ele元素
- boolean addAll(int index, Collection eles): 从index位置开始将eles中的所有元素添加进去。
- Object get(int index): 获取指定index位置的元素
- int indexOf(Object obj): 返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj): 返回obj在当前集合中末次出现的位置
- Object remove(int index): 移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele): 设置指定index位置的元素为ele
- List subList(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合。
@Test public void test1(){ ArrayList list = new ArrayList(); list.add(123); list.add("acd"); list.add(new Person("Tom",12)); System.out.println(list); //[123, acd, Person@27dd1a] list.add(2,"dddd"); System.out.println(list); //[123, acd, dddd, Person@27dd1a] System.out.println(list.get(2)); //dddd System.out.println(list.set(2,"DDDD")); //dddd System.out.println(list); //[123, acd, DDDD, Person@27dd1a] }
区分List中的remove(int index) 和 remove(Object obj) // remove(2) remove(new Integer(2))
@Test public void testListRemove(){ List list = new ArrayList(); list.add(1); list.add(2); list.add(3); updateList(list); System.out.println(list); //[1,2] } private static void updateList(List list){ list.remove(2); // index位置 }
@Test public void testListRemove(){ List list = new ArrayList(); list.add(1); list.add(2); list.add(3); updateList(list); System.out.println(list); // [1,3] } private static void updateList(List list){ list.remove(new Integer(2)); // 删除元素为2 }