List集合的学习
-
特点:有序(存储顺序和取出顺序是一致的),可重复
List A = new ArrayList(); A.add("a"); A.add("b"); A.add("c"); A.add("d"); A.add("d"); for (int i = 0; i < A.size(); i++) { System.out.println(A.get(i)); }
-
关于 迭代器 与Collection的区别
- 返回的是自己特有的 ListIterator extends Iterator
- 增强了 iterator 的方法,不止于 hasNext() next() remove()
- 列表有双向列表,所有增加了 pervious() hasPervious();
add(),set() 可以添加、设置元素
List 集合常用子类
- List集合的常用子类 三个
- ArrayList
- 底层的数据结构是数组。
线程不安全的
- 底层的数据结构是数组。
- LinkedList
- 底层结构是链表。
线程不安全的
- 底层结构是链表。
- Vector
- 底层结构是数组。
线程安全的
- 底层结构是数组。
- ArrayList
Set 集合常用的子类
- HashSet
- 底层结构是哈希表(每一个元素为一个链表的数组)
- TreeSet
- 底层结构是红黑树(是一个自平衡的二叉树)
- 保证元素的排序方式
- LinkedHashSet
- 底层结构是哈希表和链表
ArrayList
-
属性
-
初始化容量
private static final int DEFAULT_CAPACITY = 10;
-
指定构造函数容量为0时返回的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
-
用户不指定,默认返回的空数组,也就是空构造
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
-
ArrayList的实际大小
private int size;
-
底层数组
:当第一次Add元素时,这个数组会扩容到初始化容量DEFAULT_CAPACITYtransient Object[] elementData; // non-private to simplify nested class access
-
-
构造函数方面
- 如果用户指定了初始容量,那么就会构造处对应容量的对象数组,若初始化容量为0时,就返回的是
EMPTY_ELEMENTDATA
- 如果用户不指定初始容量(空构造),返回的是
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 如果用户指定了初始容量,那么就会构造处对应容量的对象数组,若初始化容量为0时,就返回的是
-
Add方法
-
add(E e) 直接添加元素的
-
注意点
- 检查是否需要扩容
- 插入元素
-
底层步骤
-
检查容量是否需要扩容
-
容量足够,那么直接添加
-
容量不足,扩容 核心是根据 size 和 length 判断,个数-长度>0那么就需要扩容
- 扩容到原来的1.5倍
- 如果第一次扩容的容量,还是比minCapacity小,那么直接扩容到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) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
-
-
-
add(int index, E e) 将元素插入到指定的位置
-
注意点
- 检查角标
- 检查是否需要扩容
- 插入元素
-
底层步骤
-
检查角标是否越界
-
private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
-
如果插入位置,都已经超出了元素个数了,或者为负数,都是越界的情况
-
-
检查是否需要扩容
- 调用的就是 add 的扩容检查方法
-
调用
System.arraycopy(elementData, index, elementData, index + 1, size - index);
拷贝数组,其实本质就是所有的下表往后移了一位的处理
-
在指定位置赋值元素,size++
-
elementData[index] = element; size++;
-
-
-
-
get(int index) 获取指定位置的元素
-
注意点
- 检查角标
- 返回元素
-
底层步骤
-
检查角标,调用的就是 add(index,e)的检查越界的方法
-
返回元素
-
不是直接底层数组[下标]
-
而是又包了一层,可能是做后期的扩展
-
E elementData(int index) { return (E) elementData[index]; }
-
public E get(int index) { rangeCheck(index); return elementData(index); }
-
-
-
-
-
set(int index, E e) 设置指定位置的元素
-
注意点
- 检查角标
替代元素
返回旧值
-
底层实现步骤
-
检查角标,同add(index,e)
-
替代元素
-
让一个 变量等于 索引处的值
E oldValue = elementData(index);
-
替换索引处的元素
elementData[index] = element;
-
返回旧值
return oldValue;
-
源码
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
-
-
-
-
remove(int index) 删除指定位置的元素
-
注意点
- 检查角标
删除元素
- 计算出需要移动的个数,并且移动
- 设置为
null
,让GC回收 - 返回删除元素
-
底层实现
-
检查角标,同add(index, e)
-
删除元素
- 通过数组拷贝,index位置直接被下一个覆盖带掉
-
计算移动个数
int numMoved = size - index - 1;
-
-
-