集合
为什么要使用集合?
弥补数组不足之处:
-
长度必须使用前指定,且不能更改
-
储存的的数据类型必须统一
-
数据增删比较麻烦
集合的优点:
-
动态储存任意长度任意类型数据,包括null
-
提供一系列操作数据对象的方法add,remove,set,get等操作更简洁
框架体系
java集合主要分为两大类 单列集合,双列集合
所谓单列集合就是数据存储为单个对象,例如Colletion接口的实现类,而双列集合则是以键值对的形式存储,例如Map接口的实现类
Collection接口
Collection接口继承了Iterable接口,实现了可迭代,所以Collection接口所有实现子类均可以使用迭代器遍历
Collection接口
Collection接口常用方法
List接口实现子类对象均可使用
boolean add•(E e)
单个元素添加。
boolean addAll•(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合中。
void clear()
从此集合中删除所有元素。
boolean contains•(Object o)
如果此collection包含指定的元素,则返回 true 。
boolean containsAll•(Collection<?> c)
如果此集合包含指定集合中的所有元素,则返回 true 。
boolean equals•(Object o)
将指定对象与此集合进行比较以获得相等性。
Iterator<E> iterator()
返回此集合中元素的迭代器。
boolean remove•(Object o)
从此集合中移除指定元素的单个实例(如果存在)。
boolean removeAll•(Collection<?> c)
删除此集合的所有元素,这些元素也包含在指定的集合中。
int size()
返回此集合中的元素数。
Object[] toArray()
返回包含此集合中所有元素的数组。
List接口常用方法
void add•(int index, E element)
将指定元素插入此列表中的指定位置(可选操作)。
boolean add•(E e)
将指定的元素追加到此列表的末尾(可选操作)。
boolean addAll•(int index, Collection<? extends E> c)
将指定集合中的所有元素插入到指定位置的此列表中(可选操作)。
boolean addAll•(Collection<? extends E> c)
将指定集合中的所有元素按指定集合的迭代器(可选操作)返回的顺序追加到此列表的末尾。
void clear()
从此列表中删除所有元素(可选操作)。
boolean contains•(Object o)
如果此列表包含指定的元素,则返回 true 。
boolean containsAll•(Collection<?> c)
如果此列表包含指定集合的所有元素,则返回 true 。
boolean equals•(Object o)
将指定对象与此列表进行比较以获得相等性。
E get•(int index)
返回此列表中指定位置的元素。
int indexOf•(Object o)
返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
boolean isEmpty()
如果此列表不包含任何元素,则返回 true 。
Iterator<E> iterator()
以适当的顺序返回此列表中元素的迭代器。
int lastIndexOf•(Object o)
返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。
E remove•(int index)
删除此列表中指定位置的元素(可选操作)。
boolean remove•(Object o)
从该列表中删除指定元素的第一个匹配项(如果存在)(可选操作)。
boolean removeAll•(Collection<?> c)
从此列表中删除指定集合中包含的所有元素(可选操作)。
E set•(int index, E element)
用指定的元素替换此列表中指定位置的元素(可选操作)。
int size()
返回此列表中的元素数。
default void sort•(Comparator<? super E> c)
根据指定的Comparator引发的顺序对此列表进行排序。
List<E> subList•(int fromIndex, int toIndex)
返回指定的 fromIndex (包含)和 toIndex (不包括)之间的此列表部分的视图。
Object[] toArray()
以适当的顺序(从第一个元素到最后一个元素)返回包含此列表中所有元素的数组。
<T> T[] toArray•(T[] a)
以适当的顺序返回包含此列表中所有元素的数组(从第一个元素到最后一个元素); 返回数组的运行时类型是指定数组的运行时类型。
List接口常见实现子类及特点
List接口继承了Collection接口
-
List集合实现类元素有序(添加取出顺序一致),且可重复
-
支持索引
ArrayList
底层维护了一个Object数组,线程不安全。增删效率较Vector较高
可以添加null,且多个
创建对象时选择构造器
ArrayList()
构造一个初始容量为0的空列表。
ArrayList(int initialCapacity)
构造具有指定初始容量的空列表。
扩容方式
-
无参构造初始化
-
构造一个初始容量为0的空列表。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //无参构造 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//{} }
-
添加元素时需要扩容
public boolean add(E e) { // 当前元素数量量加1,检查容量是否够用,选择是否扩容 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } transient Object[] elementData; // non-private to simplify nested class access private void ensureCapacityInternal(int minCapacity) {//minCapcity 需要的最小容量,即 size+1 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static final int DEFAULT_CAPACITY = 10; private static int calculateCapacity(Object[] elementData, int minCapacity) { //如果当前数组是无参构造时初始化的空数组 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //返回 10 和所需容量的最大值 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { //参数modCount记录了 某个List改变大小的次数,如果modCount改变的不符合预期,那么就会抛出异常。 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);//当前容量1.5倍,如果是空还是0 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);//扩容底层实际上还是数组扩容 }
-
-
有参构造初始化
有参构造初始化给定初始容量,添加元素容量不足时在当前容量基础上1.5倍扩容
Vector
语法和ArrayList相同,除了同步情况是线程安全的,及无参构造初始化默认容量10,后按当前容量2倍扩容,也可在构造方法中指定扩容增量,如果指定大小则扩容直接为指定大小两倍
构造方法 |
---|
Vector() 构造一个空向量,使其内部数据数组的大小为 10 ,标准容量增量为零。 |
Vector(int initialCapacity) 构造具有指定初始容量并且其容量增量等于零的空向量。 |
Vector(int initialCapacity, int capacityIncrement) 构造具有指定的初始容量和容量增量的空向量。 |
//扩容核心代码
newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);//capacityIncrement = 0
LinkedList
语法和ArrayList相同,底层使用双向链表,每个节点由数据item,前驱prev,后继next组成
实现了双向链表和双端队列的特点可以添加任意元素(可重复),包括 null
线程不安全,没有实现同步
构造器
构造器 | 描述 |
---|---|
LinkedList() | 构造一个空列表。 |
LinkedList(Collection<? extends E> c) | 按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。 |
底层源码
//初始化空列表
public LinkedList() {
}
//链表属性
transient int size = 0; // 节点数量
transient Node<E> first;//链表第一个节点
transient Node<E> last;//链表最后一个节点
//添加元素
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;//保存当前最后一个节点
final Node<E> newNode = new Node<>(l, e, null);//初始化新节点,前驱,数据,后继
last = newNode;//last指向新节点,充当最后一个节点
if (l == null)//如果未添加前最后一个节点是空,即是空列表
first = newNode;//新节点为第一个节点
else
l.next = newNode;//否则把新节点放到最后
size++;//数据个数
modCount++;//修改次数
}
//删除,以按照索引删除为例
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {//判断索引是否符合要求
return index >= 0 && index < size;
}
Node<E> node(int index) {
// 折半处理两种情况
if (index < (size >> 1)) {//左半边
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {// 待删除节点是第一个
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
ArratList,Vector,LinkedList区别
底层实现 | 优缺点 | 线程安全 | 扩容方式(无参构造初始化) | |
---|---|---|---|---|
ArrayList | 可变数组 | 增删效率低查找效率高 | 否(相比于Vector增删效率高) | 0->10->1.5倍扩容 |
Vector | 可变数组 | 增删效率低查找效率高 | 是 | 10->2倍扩容 |
LinkedList | 链表 | 增删效率高查找效率低 | 否 | / |
效率
查询速度上看: Vector ≈ ArrayList > LinkedList 增删改速度上看:Vector < ArrayList < LinkedList
遍历方式
-
for循环,使用size()方法获取长度,get()方法获取元素
-
增强for循环
-
迭代器(因为Colletion接口继承了Iterable接口)
迭代器使用需要Iterator()方法初始化迭代器,该方法在Collection的父接口Iterable中定义了
Iterator<T> iterator() 返回 T类型的元素的迭代器。 Iterator iterator = List1.iterator(); //每次往后迭代时需要判断是否到最后一个元素了,使用hasnext()方法 while (iterator.hasNext()) { Object next = l.next(); System.out.println(next); }
下面应该是Collection接口的子接口Set接口,不过Set接口常用实现子类TreeSet和HashSet在了解完Map接口后再了解更友好,所以放在Map接口后面讲!