1. 简介
集合类型,不定长的"数组",支持对“数组”进行灵活的操作。
实现上,有基于数组的集合(ArrayList
)和基于链表的集合(LinkedList
)。
2. 概念
- 动态扩容
- MAX_ARRAY_SIZE,最大数组长度=Integer.MAX_VALUE - 8
- 双向链表之
Node
,快捷方式之first
、last
- 简单的二分法:
node(int index)
- 双向队列
3. 源码
- ArrayList
-
toArray(T[] a)
// 保证new的a的长度,要能cover住原list的大小。
// 否则需要使用反射构造一个新的数组if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // codes in Arrays.copyOf Array.newInstance(newType.getComponentType(), newLength);
-
动态扩容
calculateCapacity
// 默认构造函数生成的list,第一次add,容量扩大到10
// 后续按1.5倍大小扩容private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
-
子集合
subList(from, to)
// 返回的是SubList,非ArrayList // 引用的父集合的数据,操作子集合,结果会体现在父集合上。 return new SubList(this, 0, fromIndex, toIndex);
-
序列化
// 1. 空集合初始化,元素数组是DEFAULTCAPACITY_EMPTY_ELEMENTDATA public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } // 2. 序列化,读到空集合,返回元素集合为 EMPTY_ELEMENTDATA private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; ... } // 3. 如此,空集合在经过序列化处理后,不能正常扩容。见第2点:calculateCapacity
- LinkedList
-
简洁的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; } }
-
简单的二分法
node(index) { if (index < (size >> 1)) { for (int i = 0; i < index; i++) ... } else { for (int i = size - 1; i > index; i--) ... } }
-
toArray or Iterator
避免并发的影响,相关的操作对象,都是先“实例化”再处理// 1. 集合转数组 addAll(index, collection c) { Object[] a = c.toArray(); // 避免add过程中,c出现并发修改,导致addAll报错 } // 2. first, last xxx() { final Node<E> f = first; }
4. 示例
-
toString
@Test public void testList2String() { List<String> list = new ArrayList<String>(3 ); list.add("1"); list.add("3"); list.add("2"); Assert.assertEquals("[1, 3, 2]", list.toString()); List<String> linklist = new LinkedList<String>(); linklist.add("1"); linklist.add("3"); linklist.add("2"); Assert.assertEquals("[1, 3, 2]", linklist.toString()); }
-
序列化后扩容失效
@Test public void testAdd2EmptyList() throws IOException, ClassNotFoundException { List<String> list = new ArrayList<String>(); // size = max(1.5*size, size+1); // elementData.length = 10 list.add("1"); Assert.assertEquals(1, list.size()); list = new ArrayList<String>(); ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(list); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(in); List<String> anoList = (List<String>) ois.readObject(); // elementData.length = 1, not 10 anoList.add("2"); // elementData.length = 2 anoList.add("3"); }
-
子集合
@Test public void testSubList() { List<String> pList = new ArrayList<String>(10); for (int i = 0; i < 10; i++) { pList.add("" + i); } // getSubList List<String> subList = pList.subList(2,3); System.out.println(subList); subList.add("bug"); Assert.assertEquals("bug", pList.get(3)); System.out.println(pList); }
-
Add All
// java.util.ConcurrentModificationException @Test public void testAddAll() { final List<String> ll = new LinkedList<>(); String sayHi = "Hello all, this is a test for linked list."; for (String s : sayHi.split("[,.\\s]+")) { ll.add(s); } new Thread(new Runnable() { @Override public void run() { for (int i =0; i< 100; i++) { ll.add(i+""); } } }).start(); List<String> anoll = new LinkedList<>(); // anoll.addAll(ll); // 此时,新集合的长度就已经固定了 for (String s : ll) { anoll.add(s); } System.out.println(anoll.size()); }
5. 总结
集合 or 数组? ArrayList or LinkedList ?
LinkedList通过增加“复杂”的数据结构,实现了高性能的随机插入和删除操作。
同时它也没有“容量”的概念,空间不需要统一分配。
对于“不定长”的,变化快的数据,LinkedList是个不错的选择?