❤写在前面
❤博客主页:努力的小鳴人
❤系列专栏:JavaSE超详总结😋
❤欢迎小伙伴们,点赞👍关注🔎收藏🍔一起学习!
❤如有错误的地方,还请小伙伴们指正!🌹
对于【10章Java集合】几张脑图带你进入Java集合的头脑风暴🔥 的拓展分析
文章目录
一、基础知识
- 特点:
有序的,允许重复元素
、顺序可以是自然排序或按对象加入到集合的顺序排序 - 遍历:
① Iterator迭代器方式
② 增强for循环
③ 普通的循环 - List接口常用方法
- JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector
二、ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList继承了AbstractList,且实现了List, RandomAccess, Cloneable, Serializable接口
ArrayList是一个可变大小的数组
🔥无参构造函数
默认情况下,数组为一个空数组
//无参构造函数
public ArrayList() {
super();//因为继承了AbstractList,所以调用AbstractList的构造函数
//这里是把数组设置为空数组对象
this.elementData = EMPTY_ELEMENTDATA;
}
🔥源码分析
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8683452581122892189L;
//默认数组大小为10
private static final int DEFAULT_CAPACITY = 10;
//空数组对象
private static final Object[] EMPTY_ELEMENTDATA = {};
//ArrayList底层基于该数组实现
private transient Object[] elementData;
//ArrayList中实际数据的大小
private int size;
//带有初始化容量大小的构造函数
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
//使数组初始化为指定容量大小
this.elementData = new Object[initialCapacity];
}
//无参构造函数
public ArrayList() {
super();
//这里是把数组设置为空数组对象
this.elementData = EMPTY_ELEMENTDATA;
}
//创建一个包含Collection的ArrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
//将当期容量值设置为实际元素个数
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
//确保ArrayList容量,如果
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
//初始化时候,elementData是为空数组对象EMPTY_ELEMENTDATA,所以会去设置minCapacity的值
if (elementData == EMPTY_ELEMENTDATA) {
//设置minCapacity值,比较minCapacity和默认容量(DEFAULT_CAPACITY=10)
//把最大值赋值给minCapacity
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确定明确的容量大小
ensureExplicitCapacity(minCapacity);
}
//确定明确的容量大小
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//最大数组大小=Integer.MAX_VALUE - 8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//扩容方法
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
三、LinkedList
双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素
- 定义
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList继承了AbstractSequentialList,并且实现了List, Deque, Cloneable, Serializable接口
LinkedList底层是基于链表实现的
- LnkedList里面定义了一个私有静态内部类Node
Node有三个成员变量,item, next, prev.从字面上理解为本身, 下一个元素,前一个元素
private static class Node<E> {
//本身
E item;
//下一个元素
Node<E> next;
//前一个元素
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
🔥原理
●LinkedList 通过双向链表去实现的,一个内部类:Entry:Entry是双向链表节点所对应的数据结构,它包括的属性有:当前节点所包含的值,上一个节点,下一个节点
●LinkedList的克隆函数,即是将全部元素克隆到一个新的LinkedList对象中。
●LinkedList实现java.io.Serializable,当写入到输出流时,先写入“容量”,再依次写入“每一个节点保护的值”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”
●由于LinkedList实现了Deque,而Deque接口定义了在双端队列两端访问元素的方法:提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false等)
🔥遍历方式
LinkedList支持多种遍历方式
package ListTest;
import java.util.ArrayList;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(7);
list.add(6);
list.add(5);
list.add(8);
list.add(9);
list.add(11);
//通过迭代器遍历
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+" ");
}
System.out.println("\n--------------------------");
//for循环遍历
for (Integer integer : list) {
System.out.print(integer+" ");
}
System.out.println("\n--------------------------");
//随机访问,通过list.get(i)获得索引值去遍历,不建议
for(int i=0;i<list.size();i++) {
System.out.print(list.get(i)+" ");
}
}
}
随机访问的方式去遍历LinkedList是低效的,采用逐个遍历的方式较高效
🔥常用方法
四、Vector
Vector 是矢量队列,底层是数组
Vector中的操作是线程安全的,效率不高
🔥构造函数
//Vector和ArrayList一样,底层基于该数组实现
protected Object[] elementData;
//这个相当于ArrayList里面的size
protected int elementCount;
//当Vector的大小大于其容量时,Vector的容量自动增加的量。
protected int capacityIncrement;
//带容量和容量自动增加量的参数的构造函数
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
//初始化数组
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//给定容量的构造函数
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//无参构造函数
public Vector() {
//初始化数组,大小为10
this(10);
}
在new Vector()的时候,vector内数组已经被初始化了,并且数组的长度为10
🔥原理
●Vector实际通过一个数组保存数据,当构造Vecotr时使用默认构造函数,则Vector的默认容量大小是10
●当Vector容量不足以容纳全部元素时,Vector的容量会增加。若容量增加系数 >0,则将容量的值增加“容量增加系数”;否则,将容量大小增加一倍。
●Vector的克隆函数,即是将全部元素克隆到一个数组中
🔥遍历方式
- 通过迭代器遍历。即通过Iterator去遍历
Integer value = null;
int size = vec.size();
for (int i=0; i<size; i++) {
value = (Integer)vec.get(i);
}
- 随机访问,通过索引值去遍历
Integer value = null;
int size = vec.size();
for (int i=0; i<size; i++) {
value = (Integer)vec.get(i);
}
- for循环
Integer value = null;
for (Integer integ:vec) {
value = integ;
}
- Enumeration遍历
Integer value = null;
Enumeration enu = vec.elements();
while (enu.hasMoreElements()) {
value = (Integer)enu.nextElement();
}
🔥常用方法
五、ArrayList LinkedList Vector 三者区别
主要区别在于实现方法的不同,所有对不同的操作具有不同的效率
- ArrayList
ArrayList是一个可以改变大小的,线程不同步(不支持并发)的数组,内部值可以为null。 当更多的元素加入到ArrayList中时,大小会自动增加,内部元素可以直接通过get/set方法进行访问,因为ArrayList本质上即使一个数组 - LinkedList
LinkedList底层是基于双链表实现的,在添加和删除元素时具有比ArrayList更好的性能。但是在get/set方面要弱于ArrayList(前提是这些对比是在数据量很大或者操作很繁琐的情况下),LinkedList内部值可以为null,但是当我们调用值为null的元素的时候会出现NullPointerException
LinkedList更适合①没有大量的随机访问操作②有大量的add/remove操作。 - Vector
属于线程同步(支持并发)的数组,并且内部值也可以为null
🎁总结:
ArrayList和Vector它们底层实现为数组,值可为null, ArrayList不支持并发,Vector支持并发;LinkedList底层基于双链表,所以在add/remove元素时比ArrayList要快
👌 作者算是一名Java初学者,文章如有错误,欢迎评论私信指正,一起学习~~
😊如果文章对小伙伴们来说有用的话,点赞👍关注🔎收藏🍔就是我的最大动力!
🚩不积跬步,无以至千里,书接下回,欢迎再见🌹