文章开始之前,首先简单回顾一下数据结构中定义的物理结构和逻辑结构。
物理结构:
- 顺序存储结构
- 链式存储结构
逻辑结构
- 线性结构:一般线性表、栈、队列、字符串、数组、广义表
- 非线性结构:树、图
关于集合命名规则,Java中集合的命名大体上可以归类为AbEf的形式: Ab代表物理结构,Ef代表逻辑结构,但是这只是对于大多数的集合名有效。如ArrayList,就是用数组实现线性表;而LinkedList就是用链表实现的线性表。
最后补充一个必知的native方法:
- arraycopy()@java.lang.System.class
- public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
- 功能:将src中的srcPos~srcPos+length-1的数据复制到dest中的destPos~destPos+length-1位置处;
顺序存储结构实现的集合(即数组实现)
A、B、C、D是按照底层实现的相似性进行划分的
PartA-线性表&栈
ArrayList:
- 内部存储了一个Object[];提供随机访问;
- ArrayList中数据的移动大多是利用System.arraycopy();方法实现,该方法是native方法;
- 扩容;
- 默认大小是10;
- 最大容量Integer.MAX_VALUE - 8;
- 扩容触发条件:当前要求容量大小大于当前容量大小
- 正常扩容newCapacity = oldCapacity + (oldCapacity >> 1);
- 使用System.arraycopy将数据全部移动到新的数组中
- 缩容:手动调用trimToSize()方法,会缩到当前数组实际存储的数据大小
Vector:
- Vector与ArrayList共同点
- 两者实现的功能相同,方法的实现也基本一致;
- 都具有SubList内部类和Itr迭代器内部类
- 都能够扩容和缩容
- Vector与ArrayList区别:
- 线程安全:Vector对大部分的方法使用了关键字Synchronized声明
- 额外的%element%方法:这些方法会被Stack类所使用;
Stack:
- Stack主要使用的是Vector<E>的方法进行具体的实现,自身没有状态量
- 线程安全:继承Vector,故也是线程安全的
- pop方法:使用elementAt+removeElementAt
- push方法:使用addElement
- search方法:从后往前搜索调用lastIndexOf
PartB-队列
ArrayDeque:
- 存储有一个Obejct[]数组;数组长度必须是2的幂次,因为这样就方便取余运算;
- 有一个head和tail指针;
- tail =(tail+1)&(elements.length-1)
- head =(head+1)&(elements.length-1)
- size = (tail-head)&(elements.length-1)
- 扩容:
- 正常扩容后大小为原数组长度2倍
- 利用System.arraycopy方法实现
PriorityQueue:
- 类中存储有一个Object[]数组
- size为当前数组中存储的数据的大小
- 存入的数据类型要求实现了Comparable接口
- 数据的添加和删除操作伴随着调堆的操作;
- 具有扩容功能:
- 扩容大小:当前大小的2倍或者1.5倍
- 扩容条件:准备存入的数据量大于当前Obejct数组的长度;
PartC-Map
HashMap:
- 内部存储了一个Node<K,V>[] table;
- 内部有定义一个Node内部类和TreeNode内部类;后者是一颗红黑树
- 数组的下标(散列值)等于hash(key)&n-1;
- 会自动扩容:
- 默认大小是16;大小必须为2的幂
- 最大容量是1<<30
- 填装因子为0.75
- 扩容触发条件:一旦当前存储数据大于门限(容量*填装因子)
- 正常扩容newCapacity = oldCapacity<<1;
- 每个桶中的数据分成两拨一波继续留在原散列值下面,一波移动到原散列值+oldCapacity位置处
- 不能自动减容;
- 红黑树的利用:
- 当一个桶中的元素大于某个固定值,则将该桶中的数据由链表存储结构,转换成红黑树存储结构;
- 在此之后的所有操作交由红黑树进行处理;
- 在扩容的时候,对红黑树进行拆分的过程中,如果最后元素个数小于固定值,则将红黑树存储结构转换为普通链表存储结构
HashTable:
- 基本与HashMap类似;
- 线程安全:HashTable的大部分方法使用了Synchronized关键字进行标记
- 没有红黑树:HashTable对桶中数据的存储没有使用红黑树,因此效率相对于hashmap会差一点
- 不支持存储null:会抛出异常
HashSet:
- 内部存储了一个HashMap<E,Object>变量
- 一个简单版本的HashMap;
- 只使用了key而没有使用value部分,value永远存的都是固定的Object
PartD-String
String
- 内部存储的是一个final char[]字符数组;
- 因此不能修改它指向的字符数组
StringBuidler
- 内部存储的是一个char[]字符数组
- 因此可以修改里面的字符
- 对于数组的拷贝大多依赖于System.arraycopy的方法
- 扩容
- 默认大小为16
- 容量最大值:Integer.MAX_VALUE
- 正常情况扩容大小:int newCapacity = value.length * 2 + 2;
- 扩容触发条件:准备存储的字符数大于当前数组的长度
- 缩容:
- 为当前存储数据的大小
StringBuffer
- 与Stringbuilder类似
- 线程安全:方法大多使用了Synchronized关键字标记
链式存储结构实现的集合(即链表实现)
A、B是按照底层实现的相似性进行划分的
PartA-Map
TreeMap:
- 定义了一个内部类Entry<K,V>;该节点定义了这课红黑树的存储结构;
- 存储有该树的根节点;要删除这颗树只需要将root指向null即可;GC之后便会回收该红黑树,造成其它节点的引用不可达
- 每次插入、删除一个树中节点时;都需要对这颗红黑树进行一定的调整;
TreeSet:
- 内部存储了一个TreeSet<E,Object>对象
- 一个简单版本的TreeMap;
- 只使用了key而没有使用value部分,value永远存的都是固定的Object
PartB-List&Map
LinkedList:
- 内部存储一个双向链表;所有的操作都是对该链表进行操作
LinkedHashMap:
- 数据存储是HashMap只是有在其上面加了一个链表结构,每次对该HashMap中的数访问一次都会改变链表结构;保证链表中的最后面的数据是最近刚被访问的那个节点的数据。具有LRU算法特性