day26下
集合框架
标绿已经学习底层,深入底层主要是研究实现类底层
手撕Vector底层源码
场景:
//Vector<String> v = new Vector<>(10,5);//扩容机制是:数组原来长度 + 容量增量
Vector<String> v = new Vector<>();//扩容机制是:数组原来长度的2倍
v.add("aaa");
v.add("bbb");
v.add("ccc");
v.add("ddd");
底层
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//外部操作数(计算添加和删除的次数)
protected transient int modCount = 0;//最终4
protected AbstractList() {
}
}
public class Vector<E> extends AbstractList<E> implements List<E>{
//数组最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//数据容器 - ["aaa","bbb","ccc","ddd",null,null,null,null,null,null]
protected Object[] elementData;//new Object[10];
//元素个数(相当于ArrayList的size)
protected int elementCount;//最终4
//容量增量
protected int capacityIncrement;//0
//new对象过程
//无参构造调用一个参数的有参构造
public Vector() {
this(10);
}
//一个参数的有参构造调用两个参数的有参构造
//initialCapacity - 10
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//initialCapacity - 10
//capacityIncrement - 0
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//添加过程(加锁)
// e - 最终"ddd"
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
//minCapacity - 11
//判断
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//minCapacity - 11
//扩容
private void grow(int minCapacity) {
// oldCapacity - 10
int oldCapacity = elementData.length;
// newCapacity = 10 + 10
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
//超过最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
}
面试题
Vector的底层数据结构是什么?
Object类型的一维数组
Vector的使用场景是什么?
在多线程下使用,因为Vector方法上加锁,是线程安全的集合
Vector的扩容机制是什么?
如果容量增量大于0,扩容机制是:数组原来长度 + 容量增量
如果容量增量小于等于0,扩容机制是:数组原来长度的2倍
手撕Stack底层源码
注意:
- 研究Stack底层是如何实现栈的数据结构
- Stack底层所有的功能都依赖于父类Vector的方法,只不过Stack有自己的逻辑
场景:
Stack<String> stack = new Stack<>();
stack.push("aaa");
stack.push("bbb");
stack.push("ccc");
stack.push("ddd");
底层
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//外部操作数(记录添加和删除的次数)
protected transient int modCount = 0;
}
public class Vector<E> extends AbstractList<E> implements List<E>{
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
public synchronized int size() {
return elementCount;
}
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}//超过越界
return elementData(index);//反之获取
}
}
public class Stack<E> extends Vector<E> {
public Stack() {
}
//item - "aaa"
public E push(E item) {
addElement(item);//调用父类的addElement() -- 添加元素
return item;
}
//获取栈顶元素并删除(删除栈顶元素)
public synchronized E pop() {
E obj;
int len = size();//调用父类的size() -- 获取元素个数
obj = peek();//获取栈顶元素
//len - 1 表示最后一个元素的下标
removeElementAt(len - 1);//调用父类的removeElementAt() -- 删除最后一个元素
return obj;
}
//获取栈顶元素的下标
public synchronized E peek() {
int len = size();//调用父类的size() -- 获取元素个数
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);//调用父类的elementAt() -- 获取指定下标上的元素
}
}
手撕HashSet底层源码
注意:
- HashSet底层由HashMap实现
- HashSet的元素添加到HashMap中Key的位置
场景:
HashSet<String> set = new HashSet<>();
set.add("aaa");//将元素添加到HashMap中:map.put("aaa",PRESENT);
set.add("bbb");//将元素添加到HashMap中:map.put("bbb",PRESENT);
set.add("ccc");//将元素添加到HashMap中:map.put("ccc",PRESENT);
set.add("ddd");//将元素添加到HashMap中:map.put("ddd",PRESENT);
底层
public class HashSet<E> extends AbstractSet<E> implements Set<E>{
//HashMap存键值对(key,value),key去重,因此将值传到key位置,value位置搞个占位值
private transient HashMap<E,Object> map;//new HashMap<>();
//占位value(static final修饰,静态常量,内存中唯一)
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
//添加
//e - "ccc"
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
手撕TreeSet底层源码
注意:
TreeSet底层由TreeMap实现
TreeSet的元素添加到TreeMap中Key的位置
3.关注构造方法
场景1:(内置比较器)
TreeSet<Student> set = new TreeSet<>();
set.add(new Student("椎名空", '女', 23, "2401", "002"));
set.add(new Student("麻生希", '女', 27, "2401", "001"));
set.add(new Student("水菜丽", '女', 21, "2401", "003"));
场景2:
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
...外置比较器规则...
}
});
set.add(new Student("椎名空", '女', 23, "2401", "002"));
set.add(new Student("麻生希", '女', 27, "2401", "001"));
set.add(new Student("水菜丽", '女', 21, "2401", "003"));
底层
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>{
//外置比较器
private final Comparator<? super K> comparator;
//使用无参构造创建TreeMap,外置比较器对象就是null
public TreeMap() {
comparator = null;
}
//使用有参构造创建TreeMap,外置比较器对象由外界传入
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
public V put(K key, V value) {
//获取外置比较器
Comparator<? super K> cpr = comparator;
//注意:以下判断说明了外置比较器优先于内置比较器,因为先找的是外置比较器,如果外置比较器为null, 才执行内置比较器的比较规则
//外置比较器是否为null
if (cpr != null) {//外置比较器不为空
//执行外置比较器的比较规则
} else {//外置比较器为空
//执行内置比较器的比较规则
}
}
}
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>{
//使用无参构造创建TreeSet -- new TreeMap<E,Object>()
//使用无参构造创建TreeSet -- new TreeMap<>(comparator);
private transient NavigableMap<E,Object> m;
//占位value
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
//有参
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
// e - new Student("椎名空", '女', 23, "2401", "002")
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
}
手撕TreeMap底层源码
注意:
TreeMap的数据结构是红黑树(平衡二叉树)
红黑树的概念:
- 根节点左右两边的级数不能超过1(会自旋制衡)
- 根节点到任意的叶子节点的黑数个数是一样的
红黑树的优点:查询快(二叉树比数组查询还快)
红黑树的缺点:添加和删除慢(结构会变)
国外数据结构网站:
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
添加理解图
场景1:(内置比较器)
TreeMap<Student,String> map = new TreeMap<>();
map.put(new Student("椎名空", '女', 23, "2401", "002"),"看书");
map.put(new Student("麻生希", '女', 27, "2401", "001"),"运动");
map.put(new Student("水菜丽", '女', 21, "2401", "003"),"打游戏");
map.put(new Student("水菜丽", '女', 21, "2401", "003"),"写代码");
场景2:
TreeMap<Student,String> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if(o1.equals(o2)){
return 0;
}
int nameLen1 = o1.getName().length();
int nameLen2 = o2.getName().length();
if(nameLen1 != nameLen2){
return Integer.compare(nameLen1, nameLen2);
}
int age1 = o1.getAge();
int age2 = o2.getAge();
if(age1 != age2){
return Integer.compare(age1, age2);
}
return 1;
}
});
map.put(new Student("椎名空", '女', 23, "2401", "002"),"看书");
map.put(new Student("麻生希", '女', 27, "2401", "001"),"运动");
map.put(new Student("水菜丽", '女', 21, "2401", "003"),"打游戏");
map.put(new Student("水菜丽", '女', 21, "2401", "003"),"写代码");
底层
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>{
//外置比较器
private final Comparator<? super K> comparator;//null(第一次)
//根节点
private transient Entry<K,V> root;//null-0x001(第一次)
//元素的个数
private transient int size = 0;//0-1(第一次)-2(第二次)-3(第三次)
//外部操作数(记录添加和删除的次数)
private transient int modCount = 0;//0-1(第一次)-2(第二次)-3(第三次)
//使用无参构造创建TreeMap时,外置比较器为null
public TreeMap() {
comparator = null;
}
//使用有参构造创建TreeMap时,外置比较器不为null
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//key - new Student("水菜丽", '女', 21, "2401", "003")
//value - "写代码"
public V put(K key, V value) {
//t - null(第一次)-0x001(第二次)
Entry<K,V> t = root;
//第一次添加元素时进入的判断
if (t == null) {
compare(key, key); // null对象的检测,如果key是为null,会报空指针异常
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
//比较结果
int cmp;
//父节点
Entry<K,V> parent;
//获取外置比较器对象
Comparator<? super K> cpr = comparator;
//判断外置比较器是否为空
if (cpr != null) {//有参场景
do {//外置比较器的方法
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}else {//无参场景,元素里面实现的compareTo方法(内置比较器)
if (key == null)
throw new NullPointerException();
//Comparable<? super K> k = new Student("麻生希", '女', 27, "2401", "001")
//元素所属的类没有去实现Comparable的compareTo方法,就会报错
Comparable<? super K> k = (Comparable<? super K>) key;
do {
//parent - 0x001
parent = t;
v
//第二次:k - new Student("麻生希", '女', 27, "2401", "001")
//第三次:k - new Student("水菜丽", '女', 21, "2401", "003")
//t.key - new Student("椎名空", '女', 23, "2401", "002")
cmp = k.compareTo(t.key);//(第二次)27-21 --> 4(第三次)21-23 -->-2
if (cmp < 0)
t = t.left;//t=null(第一次)
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);//第四次,新value
} while (t != null);
}
//e - 0x002
//上一个节点记录下一个节点,形成局部双向链表
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//红黑树的制衡(自旋)
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
//补充: null对象的检测,如果key是为null,会报空指针异常
//k1 - new Student("椎名空", '女', 23, "2401", "002")
//k2 - new Student("椎名空", '女', 23, "2401", "002")
@SuppressWarnings("unchecked")
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
//映射关系类/节点类
static final class Entry<K,V> implements Map.Entry<K,V> {
K key; ------------------ 键
V value; ---------------- 值
Entry<K,V> left; -------- 左边节点的地址
Entry<K,V> right; ------- 右边节点的地址
Entry<K,V> parent; ------ 父节点的地址
boolean color = BLACK; -- 默认黑数
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
//第四次key同
//value - 写代码
public V setValue(V value) {
//oldValue = 0x003.value -> 打游戏
V oldValue = this.value;
//0x003.value = "写代码"
this.value = value;
return oldValue;//返回被替换的value(可以在代码中获取put对象输出验证)
}
}
}