ArrayList、LinkedList、CopyOnWriteArrayList
目录:
1)ArrayList
2)LinkedList
3)CopyOnWriteArrayList
1)ArrayList
概况:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
说明:
1)很多实现类都要继承一个抽象类,比如ArrayList<E>继承AbstractList<E>,那是因为如果你就实现了List<E>,那样的话得重写多少抽象方法啊。不过,一旦你继承了AbstractList<E>,因为AbstractList<E>已经重新了很多抽象方法,所以你只需要重写一部分抽象方法就可以了,不那么累嘛;
2)ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。这个至少要大概知道
代码:
Collections接口操作list
Collections.shuffle(List<T> list);//随机输出元素 Collections.reverse(List<T> list); //List元素内反转,因为list内的元素是有序的 Collections.max(List<T> list);//最大值 Collections.min(List<T> list);//最小值 Collections.sort(List<T> list);//排序 Collections.emptyList();//返回一个空集合
数组与集合之间的转换
/** * 集合转数组 * 说明:使用 toArray 带参方法,数组空间大小的 length: 1) 等于 0,动态创建与 size 相同的数组,性能最好。 * 2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。 * 3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与 2 相同。 * 4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患。 */ public static void main(String args[]) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); list.add("D"); list.add("E"); //方法一: String[] str03 = list.toArray(new String[0]); for (String str : str03) { System.out.print(str); } //方法二: Object[] objs = list.toArray(); for (Object obj : objs) { System.out.print((String)obj); } }
去掉list中的重复元素(字符串)
private static void removeRepeat() { List<String> list = new ArrayList<>(); list.add("a"); list.add("a"); list.add("b"); list.add("b"); list.add("b"); list.add("c"); list.add("c"); list.add("c"); list.add("c"); // 方法一: Set<String> set01 = new HashSet<>(list); list.clear(); list = new ArrayList<>(set01); // 方法二: Set<String> set02 = new HashSet<>(); set02.addAll(list); list.clear(); list.addAll(set02); // 方法三:创建一个新的list,没有的话就加上去 Iterator<String> iterator = list.iterator(); List<String> list2 = new ArrayList<>(); while (iterator.hasNext()) { String string = (String) iterator.next(); if (!list2.contains(string)) { list2.add(string); } } }
去掉list中的重复元素(对象)
/* * 主要注意点就是要复写Object类的equals方法,Object类的equals方法是地址值比较 * 我们要变成对象比较 */ private static void removeRepeat02() { List<Person> list = new ArrayList<>(); List<Person> list2 = new ArrayList<>(); list.add(new Person("张三",23)); list.add(new Person("李四",24)); list.add(new Person("张三",23)); list.add(new Person("李四",24)); list.add(new Person("张三",23)); list.add(new Person("王五",25)); Iterator<Person> iterator = list.iterator(); while (iterator.hasNext()) { Person person = iterator.next(); if (!list2.contains(person)) { list2.add(person); } } } class Person { public String name; public int age; ...省略构造方法,tostring方法,setget方法/* * 自定义,对象内容比较 * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } } 为什么要复写equals方法? public boolean contains(Object o) { return indexOf(o) >= 0; } 遍历list,用list里面的对象作比较,所以必须覆写equals方法。 public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; } (类似list的contains方法,list的remove方法底层也是依赖于equals方法的)
集合遍历的时候增减(Iterator)记住:ConcurrentModificationException(增减的时候只有用Iterator才不会报错)
private static void method02() { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("B"); list.add("C"); list.add("D"); list.add("E"); list.add("B"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String string = iterator.next(); if ("B".equals(string)) { iterator.remove(); } } }
分页
public class ListPage { private PageModel getListPage(List<String> listNotPage, int pageSize, int currentPage) { List<String> listPage = Collections.emptyList(); int totalSize = 0; if (!listNotPage.isEmpty()) { //对list进行分页 int fromIndex = (currentPage - 1) * pageSize; totalSize = listNotPage.size(); if (totalSize <= fromIndex) { return new PageModel(listPage, totalSize); } //所以下面的totalSize肯定大于fromIndex,所以肯定有数据,同时要保证toIndex大于FromIndex if (totalSize % pageSize == 0) { listPage = listNotPage.subList(fromIndex, currentPage * pageSize); } else { int totalPage = totalSize / pageSize + 1; listPage = listNotPage.subList(fromIndex, currentPage == totalPage ? totalSize : currentPage * pageSize); } } return new PageModel(listPage, totalSize); } } class PageModel { private List<String> list; private long totalSize; public PageModel(List<String> list, long totalSize) { super(); this.list = list; this.totalSize = totalSize; } // set,get........ }
关于set方法
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add(0, "3"); System.out.println(list); // 输出 [3, 1, 2] List<String> list02 = new ArrayList<>(); list02.add("1"); list02.add("2"); list02.set(0, "3"); System.out.println(list02); // [3, 2] }
关于removeAll与retainAll
// 去掉相同的 public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("b"); List<String> list2 = new ArrayList<>(); list2.add("a"); list2.add("b"); list2.add("d"); list.removeAll(list2); System.out.println(list); // 输出:[c] } // 保留相同的,即取交集 public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("b"); List<String> list2 = new ArrayList<>(); list2.add("a"); list2.add("b"); list2.add("d"); list.retainAll(list2); System.out.println(list); //输出:[a, b, b] }
源码:
常量:注意size 和 elementData 的区别,更多具体自己一定要去看源码的英文注释吧,你自己会看得懂的
private static final long serialVersionUID = 8683452581122892189L; private static final int DEFAULT_CAPACITY = 10; // Default initial capacity. private static final Object[] EMPTY_ELEMENTDATA = {};// Shared empty array instance used for empty instances. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 看不懂这个跟上面的,有什么区别 transient Object[] elementData; // 自己看源码英文注释 private int size; // 自己看源码英文注释 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大数组容量
构造方法
/** * 初始化集合大小创建 ArrayList 集合。当大于0时,给定多少那就初始化多大的空间;当等于0时,创建一个空数组(空间为0);当小于0时,抛出异常。 * @param initialCapacity */ 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); } } /** * Constructs an empty list with an initial capacity of ten. * 卧槽,空参构造默认占用空间为0,如果put方法向其中添加一个元素的时候,变为10, */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
add(),一定要弄清楚elementData与size的区别,其实这里的代码也没啥啊
public boolean add(E e) { ensureCapacityInternal(size + 1); // size 为该数组空间内实际上有多少个元素elementData,所以该参数就是增加之后最少的空间 elementData[size++] = e; return true; } /** * calculateCapacity(elementData, minCapacity):计算出最后要多少 capacity 之后,就进行实际的操作 * @param minCapacity 扩容后所要求的最小容量 */ private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } /** * * @param elementData 记住这里的 elementData 是一个成员变量 * @param minCapacity 扩容后所要求的最小容量 * @return 到底最后要多少 capacity */ private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); // 如果原始容量为空的话,哈哈,取默认初始化容量10和最小容量的这两个当中的最大值(空参构造向其中put一个元素初始容量变为10) } return minCapacity; // 如果原始容量不为空(不为空的话,也不可能小于 DEFAULT_CAPACITY = 10 的啦,),则返回所要求的最小容量 } /** * 这里里可以知道,数组容量和数组里面的元素个数是可以相等的,相等的情况下是不进行扩容的 * @param 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; // 这里更进一步elementData的意思 int newCapacity = oldCapacity + (oldCapacity >> 1);// 扩容,新容量为旧容量的1.5倍 if (newCapacity - minCapacity < 0) { // 1.5陪容量还不够最小容量的话,取最小容量 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); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
set(),好好体会 index >= size 中的 size
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } E elementData(int index) { return (E) elementData[index]; }
indexOf(),ArrayList中的元素可以是null元素,lastIndexOf 就是反向查找的啦 for(int i = size-1; i >= 0; i--)
/** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the lowest index <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, * or -1 if there is no such index. */ public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
get()
public E get(int index) { rangeCheck(index); return elementData(index); }
2)LinkedList
3)CopyOnWriteArrayList
参考博客:
1)【目录】集合框架目录 ---》 【集合框架】JDK1.8源码分析之ArrayList(六)
3)JDK1.8源码(五)——java.util.ArrayList 类
4)JDK1.8源码(六)——java.util.LinkedList 类 (写得真的很好,很用心啊)