集合List、ArrayList、Vector、LinkedList,map、set在下节
Collection集合
结构
Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap 接口实现类 ,没有同步, 线程不安全-
│—————–├ LinkedHashMap 双向链表和哈希表实现
│—————–└ WeakHashMap
├ ——–TreeMap 红黑树对所有的key进行排序
└———IdentifyHashMap
Collection接口实现类的特点
-
public interface Collection<E> extends Iterable<E>
-
Collection 实现子类可以存放多个元素,每个元素可以是Object
-
常用方法,以实现子类ArrayList来演示:
- add():添加指定元素
- remove():删除指定元素,可以按照index 或 object,按照object时会顺序查找,并删除找到的第一个object
- contains():查找元素是否存在,参数object
- size():获取元素个数
- isEmpty():判空
- clear():清空
- addAll():添加多个元素,参数一个collcetion对象
- containAll():查找多个元素是否存在,只有所有的元素都存在,才返回true,否则false,参数一个collcetion对象
- removeAll(): 删除多个元素,只要有一个元素能被删除掉,就返回true,一个都没被删除时,才返回false,参数一个collcetion对象
-
Collection接口遍历元素方式
-
使用Iterator(迭代器)
- )Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- )所有实现了Collection几口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
- )Iterator仅用于遍历集合,Iterator本身并不存放对象
-
iterator对象 .iterator():返回该collection的迭代器
-
List
-
List接口基本介绍
-
List接口是 Collection 接口的子接口List .java
-
List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复。List集合中的每个元素都有其对应的顺序索引,即支持索引。(所以遍历List的时候可以用迭代器,for each,和for i 索引进行遍历)
-
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
-
JDK API中List接口的实现类有:
AbstractList , AbstractsequentialList , ArrayList , AttributeList , CopyOnwriteArrayList ,LinkedList ,RoleList , RoleUnresolvedList , stack , Vector
-
-
List常用方法:
- void add(int index, Object ele):在index位置插入ele元素,不加index参数的话,默认加在最后位置
- boolean addAll(int index, Collection eles):从index位置开始讲eles中的所有元素添加进来
- Object get(int index):获取指定元素位置
- int indexOf(Object obj):获取第一次出现该obj的index
- int lastIndexOf(Object obj):返回List中末次出现obj的index
- Object remove(int index):移除指定位置的obj
- Object set(int index, Object ele):设置指定index位置的元素ele,相当于是替换,并返回index处的ele1
- List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合,范围前闭后开为 [ fromIndex, toIndex ),总个数 toIndex - fromIndex
ArrayList
ArrayList注意事项
- ArrayList 可以加入null,并且可以多个null
- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高). 方法没有synchronized修饰
- 线程不安全,在多线程情况下,不建议使用ArrayList
源码分析:
-
ArrayList中维护了一个Object类型的数组elementData.
transient Object[] elementData; -
当创建Arraylist对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加元素,则将elementData的容量扩为10, 如需要再次扩容,则扩容为elementData当前容量的1.5倍。(源码判断当前容量为0时,新建一个默认大小的数组,而默认大小
DEFAULT_CAPACITY
是被final
修饰定义的int 10,若扩容时,当前容量不为0,则扩容1.5倍,并将旧数据copy到新的数组里面。当然,这也说明如果刚开始new ArrayList对象的时候,如果传给构造器有初始容量且大于0,那么第一次扩容,就会按照1.5倍进行扩容,但同时,每次扩容容量至少增加1个容量。如:初始的设置容量为1,那么添加第二个元素时,1 + 1>>1 = 1,1 - 1 = 0按1.5倍计算,就算出需要扩充的容量为0,这时候0是小于minCapacity - OldCapacity
的,所以按最小扩容1个容量,去扩容)//扩容源码 JDK14 //minCapacity的实参为 size + 1(当前已经存储的元素个数 + 1) private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity >> 1 /* preferred growth */); return elementData = Arrays.copyOf(elementData, newCapacity); } else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; } } //ArraysSupport.newLength() public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); }
-
如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
Vector
-
Vector类的定义说明
public class vector<E>extends AbstractList<E>
implements List<E>,RandomAccess,cloneable,serializable
-
Vector底层也是一个对象数组,protected Objectl] elementData;
-
Vector是线程同步的,即线程安全, Vector类的操作方法带有synchronized
public synchronized E get(int index) { //synchronized 有synchronized修饰 if (index >= elementCount) throw new ArraylndexOutOfBoundsException(index); return elementData(index); }
-
在开发中,需要线程同步安全时,考虑使用Vector
-
默认容量大小
通过第三个构造函数,可以看出,无参的话,默认设置初始容量为10
//源码 JDK14 public Vector(int initialCapacity, int capacityIncrement) { //initialCapacity: 初始容量 //capacityIncrement:每次扩容时,扩容多大(但实际扩容这个值只是用来参考,并不一定真的就扩容这么大,下面扩容部分能详细看到) super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; } /** * Constructs an empty vector with the specified initial capacity and * with its capacity increment equal to zero. * * @param initialCapacity the initial capacity of the vector * @throws IllegalArgumentException if the specified initial capacity * is negative */ public Vector(int initialCapacity) { this(initialCapacity, 0); } /** * Constructs an empty vector so that its internal data array * has size {@code 10} and its standard capacity increment is * zero. */ public Vector() { this(10); }
-
扩容源码
//源码 JDK14 //扩容首先在这个无参函数进去,然后调下面有参数的grow函数 private Object[] grow() { return grow(elementCount + 1); } private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ //这里看出来,它会将 "扩容一倍所能增加的容量(也就是oldCapacity)"和 // "设置的固定扩容大小" 进行比较,谁大则用谁做参数传进去 capacityIncrement > 0 ? capacityIncrement : oldCapacity /* preferred growth */); return elementData = Arrays.copyOf(elementData, newCapacity); } //ArraysSupport 类的静态方法 //这里主要处理 扩容量完有可能超过了最大阈值的情况 和 扩容增加的容量未超过1 public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); } private static int hugeLength(int oldLength, int minGrowth) { int minLength = oldLength + minGrowth; if (minLength < 0) { // overflow throw new OutOfMemoryError("Required array length too large"); } if (minLength <= MAX_ARRAY_LENGTH) { return MAX_ARRAY_LENGTH; } return Integer.MAX_VALUE; }
-
Vector与ArrayList对比
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2开始有 | 不安全,效率高 | 如果有参构造,则扩容1.5倍,如果是无参,第一次扩容10,后面扩容1.5倍 |
Vector | 可变数组 | jdk1.0开始有 | 安全,效率不高 | 如果有参构造,指定大小,则扩容(2倍和增长capacityIncrement)较大的哪个,如果是无参,默认初始容量10,满了以后,按(2倍和增长capacityIncrement较大的那个)扩容 |
LinkedList
特点:
- LinkedList底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
底层结构:
-
LinkedList底层维护了一个双向链表.
-
LinkedList中维护了两个属性first和last分别指向首节点和尾节点
-
每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
-
LinkedList的元素的添加和删除,不是通过数组完成的(因为删除元素只需要动其中的需要删除的节点,后不需要移位其他元素),相对来说效率较高。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JrlF1oeG-1646660444022)(C:\Users\93468\AppData\Roaming\Typora\typora-user-images\双向链表.png)]
源码:
-
成员属性
transient int size = 0;//链表长度 /** * Pointer to first node. */ transient Node<E> first;//链表头 /** * Pointer to last node. */ transient Node<E> last;//链表尾
-
构造器
无参的话,真的就啥都没做,初始化完,first和last就是默认的null
有参的话,是一个Collection类型参数,会把调无参构造,并将该集合里的所有元素都添加到LinkedList里面
public LinkedList() { } public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
-
add():添加
进来直接调用了LinkList的
linkLast
方法,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; if (l == null) //如果是第一次添加节点 first = newNode; else //如果不是第一次添加节点 l.next = newNode; size++; modCount++; }
-
remove():删除
无参默认删除第一个节点,并返回该节点的element
public E remove() { return removeFirst(); } public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
其他的比较多,不一一列举了,不过总体就是数据结构里面的双向链表的各种操作。
-
ArrayList与LinkedList的比较
底层结构 增删的速率 改查的效率 ArrayList 可变数组 较低、数组扩容 较高 LinkedList 双向链表 较高、通过链表追加 较低