1. 集合体系图
Collection接口,Collection接口有两个重要的子接口 1.List 2. Set 。这俩的实现子类都是单列集合,即存放的都是单个单个的数据
Map接口的实现子类是双列集合,即存放数据的形式是以key-value的形式进行存放
2. Collection
- collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序的(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
2.1 Collection的常用方法
略
2.2 迭代器遍历
Iterator接口的主要方法
public interface Iterator<E> {
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next();
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
* only once per call to {@link #next}. The behavior of an iterator
* is unspecified if the underlying collection is modified while the
* iteration is in progress in any way other than by calling this
* method.
*
* @implSpec
* The default implementation throws an instance of
* {@link UnsupportedOperationException} and performs no other action.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this iterator
*
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* Performs the given action for each remaining element until all elements
* have been processed or the action throws an exception. Actions are
* performed in the order of iteration, if that order is specified.
* Exceptions thrown by the action are relayed to the caller.
*
* @implSpec
* <p>The default implementation behaves as if:
* <pre>{@code
* while (hasNext())
* action.accept(next());
* }</pre>
*
* @param action The action to be performed for each element
* @throws NullPointerException if the specified action is null
* @since 1.8
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
- Iterator仅仅用于遍历集合,Iterator本身不存放对象
Iterator的使用实例(遍历ArrayList)
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
list.add(1);
list.add("zjh");
list.add(true);
list.add(new Date());
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
注意:在调用next()方法之前必须要调用hasNext()方法进行检测,如果不调用hasNext()且下一条记录无效就会抛出 Exception in thread “main” java.util.NoSuchElementException
注意:当已经退出while循环后,如果直接调用next()方法,会出现NoSuchElementException
因为经过while循环后,游标已经指向最底部了。如果要继续重新遍历,则需要重新调用iterator()方法来获取迭代器
2.3 增强for循环
语法
for (元素类型 元素名: 集合名或者数组名){
访问元素
}
实例
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
list.add(1);
list.add("zjh");
list.add(true);
list.add(new Date());
for (Object o : list) {
System.out.println(o);
}
}
3. List
3.1 List接口基本介绍
List接口是Collection接口的子接口
- List集合类中元素有序(即添加的顺序和取出的顺序一致)、且可重复
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
list.add(1);
list.add("zjh");
list.add("zjh");
list.add(true);
list.add(new Date());
System.out.println(list);
}
- List集合中的每个元素都有其对应的顺序索引,即支持索引取出
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
list.add(1);
list.add("zjh");
list.add("zjh");
list.add(true);
list.add(new Date());
System.out.println(list.get(0));
}
- List容器中的每个元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
3.2 ArrayList底层操作机制和源码分析
3.2.1 总结(很重要)
- ArrayList中维护了一个Object类型的数组elementData。
-
当创建对象时,如果使用的是无参构造器,则初始化elementData容量为0(jdk7是10)
-
当添加元素时,先判断是否需要扩容,如果需要扩容,则调用grow()方法,否则直接添加元素到合适位置
- 如果使用的是无参构造器,如果是第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容为elementData的1.5倍
- 如果使用的是指定容量的capacity的构造器,则初始化elementData的容量为capacity
- 如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容为elementData的1.5倍
3.2.2 分析源码
3.2.2.1 基础代码如下(ArrayList空参构造器)
public class ArrayListSource {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
for (Object o : list) {
System.out.println(o);
}
}
}
在创建ArrayList上打断点
进入到第一个for循环中,进行int类型的装箱操作
进入到add()方法,在add()方法中,会先确认容量是否符合要求即ensureCapacityInternal方法
进入ensureCapacityInternal()方法
进入ensureExplicitCapacity方法中,在上图,ensureExplicitCapacity方法中的modCount是记录扩容的次数
如果minCapacity-elementData.length>0就进行grow扩容方法
minCapacity就是最小容量,elementData.length就是ArrayList底层存放元素的数组的长度
进入到grow方法中,先将原来的elementData.length赋值给oldCapacity,然后oldCapacity + (oldCapacity >> 1)赋值给newCapacity。oldCapacity + (oldCapacity >> 1)等价于oldCapacity + 0.5oldCapacity ,即1.5oldCapacity
之后就是进行赋值扩容操作
接下来的第二个for循环的扩容操作同理
3.2.2.2 基础代码如下(ArrayList有参构造器)
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>(2);
这里就直接构造有长度的数组
3.3 Vector底层操作机制和源码分析
3.3.1 总结
- Vector底层也是一个对象数组
protected Object[] elementData;
- Vector是线程同步的,即线程安全的,Vector类的操作方法会带有synchronized
- 在开发中,需要线程同步安全的时候,考虑使用Vector
- 无参构造器,默认长度为10,扩容按照2倍进行扩容
- 有参构造器,长度为构造器参数,扩容按照2倍进行扩容
3.3.2 源码分析
3.3.2.1 扩容操作
public static void main(String[] args) {
Vector vector = new Vector();
vector.add(1);
}
ensureCapacityHelper(elementCount + 1);来确定容量是否需要扩容
如果需要的容量>实际的容量,则进行扩容操作
当capacityIncre(默认为0)不大于0的时候,每次扩容为原来的两倍,如果大于0,则扩容为为原来的容量+capacityIncre
那么如何修改capacityIncre的值呢?
Vector的另一个构造器可以修改capacityIncre的值。
3.3.2.2 空参和有参构造器
空参
Vector vector = new Vector();
有参
Vector vector = new Vector(100);
Vector vector = new Vector(100,100);
3.3.3 ArrayList与Vector对比
3.4 LinkedList
3.4.1 结论
- LinkedList底层实现类双向链表和双端队列的特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全
3.4.2 LinkedList底层结构
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点
-
每个节点(Node对象)里面又维护了prev,next,item三个属性,通过prev指向前一个,next指向后一个节点来实现双向链表
-
LinkedList不是通过数组来实现的,相对来说效率较高
3.4.3 源码分析
3.4.3.1 空参构造器
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(100);
}
3.4.3.2 add()方法
接下来进行add()操作
add()方法里又linkLast()方法
- 将尾节点赋值给节点l
- 创建一个新节点,把值放入新节点newNode中,prev指向l,next指向null完成链接
- 最后把last指向newNode
- 如果l==null,就说明newNode为first节点
- 如果l不是null,就把l的next指向newNode
- 将节点数size++,modCount++
注意:为什么上图中last==null,因为这是第一次add操作
3.4.3.3 remove()方法
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(100);
list.remove();
}
remove()方法没有参数默认删除第一个方法
进入removeFirst()方法
判断first是否为null,如果为null就抛出异常,否则就调用unlinkFirst方法,参数为first节点
- 先获取到头节点f的值
- 将f.next赋值给next
- 将f.item == null f.next == null,这里就彻底断掉头节点和后面的关系,但是后面的节点和头节点还有关系
- 将first指向next
- 如果next == null,就说明已经没有节点了,所以last == null
- 反之将next.prev=null,现在后面的节点(即本节点)和之前的头节点没有关系
- size–,modCount++,返回删除的节点值