Java集合底层原理

news/2024/11/29 3:46:50/

目录

  • ArrayList集合源码
    • 创建ArrayList集合
    • 扩容机制
  • LinkedList集合源码
    • 添加数据
  • 迭代器源码
  • HashSet底层原理
  • HashMap源码
    • 创建HashMap对象
    • 添加元素
  • TreeMap源码
    • 基本属性与构造器
    • 添加元素

以下源码来自JDK11

ArrayList集合源码

创建ArrayList集合

/*
无参构造,返回一个空数组
参数:elementData - ArrayList集合维护的数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/*
* 有参构造
* 构造一个具有指定初始容量的空数组。
* 参数:initialCapacity - 列表的初始容量
* 抛出:IllegalArgumentException – 如果指定的初始容量为负
* */
public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+                initialCapacity);}
}

扩容机制

	//第一次添加元素public boolean add(E e) {modCount++;//记录集合被修改的次数,防止多个线程同时修改/*第一个参数:添加的元素第二个参数:数组名第三个参数:集合长度/现在元素的插入位置索引*/add(e, elementData, size);return true;}private void add(E e, Object[] elementData, int s) {//如果集合长度与数组长度相同,则需要扩容if (s == elementData.length)elementData = grow();elementData[s] = e;//赋值size = s + 1;//集合长度加一,亦表示下个元素插入位置}private Object[] grow() {return grow(size + 1);//传入参数表示添加完当前元素后的最小容量}private Object[] grow(int minCapacity) {/*copyOf方法会创建一个长度为newCapacity(minCapacity)的新数组,并将elementData数组中的元素全部复制到新数组当中去*/return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));}private int newCapacity(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//记录老容量//新容量等于老容量加上老容量的一半,即老容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);//作新容量是否小于等于最小容量判断if (newCapacity - minCapacity <= 0) {//如果数组是初始的空数组(即第一次添加元素),返回默认容量大小10和最小容量值两者中的最大值if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)return Math.max(DEFAULT_CAPACITY, minCapacity);/*如果最小容量值小于0,抛出内存溢出异常,最小容量超过整数最大值Integer.MAX_VALUE时就会内存溢出*/if (minCapacity < 0) // overflowthrow new OutOfMemoryError();//返回最小容量值return minCapacity;}/*新容量值大于最小容量值时,判断新容量值是否小于等于数组容量最大值,满足则返回新容量值,否则使用hugeCapacity方法确保不会扩容得太大*/return (newCapacity - MAX_ARRAY_SIZE <= 0)? newCapacity: hugeCapacity(minCapacity);}private static int hugeCapacity(int minCapacity) {/*如果最小容量值小于0,抛出内存溢出异常,最小容量超过整数最大值Integer.MAX_VALUE时就会内存溢出*/if (minCapacity < 0) // overflowthrow new OutOfMemoryError();//判断最小容量值是否超过了数组最大容量值,超过则返回整数最大值,否则返回数组最大值return (minCapacity > MAX_ARRAY_SIZE)? Integer.MAX_VALUE: MAX_ARRAY_SIZE;}

LinkedList集合源码

添加数据

    public boolean add(E e) {linkLast(e);return true;}void linkLast(E e) {//记录尾指针所指向的元素final Node<E> l = last;//创建新节点,将l作为新节点的前一个节点,指向后一个节点的指针赋值为nullfinal Node<E> newNode = new Node<>(l, e, null);//将新节点赋值给尾指针last = newNode;//如果尾指针初始为null,说明当前插入的节点是第一个节点,让first指向它if (l == null)first = newNode;else//当前插入的不是第一个节点,将新节点赋值给插入前的尾结点的next指针l.next = newNode;size++;modCount++;}//Node类是LinkedList的静态内部类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;}}

迭代器源码

    ArrayList<String> strings = new ArrayList<>();strings.add("aaa");strings.add("bbb");strings.add("ccc");//获取迭代器对象Iterator<String> iterator = strings.iterator();//调用Itr的无参构造,返回一个迭代器对象public Iterator<E> iterator() {return new Itr();}//ArrayList的内部类private class Itr implements Iterator<E> {int cursor;       // 光标,迭代器的指针,初始值为0int lastRet = -1; // 上一个被遍历元素的索引int expectedModCount = modCount;//创建迭代器时,集合被添加/删除的总次数// 无参构造Itr() {}//光标不等于集合元素总数时,光标所指位置有值public boolean hasNext() {return cursor != size;}@SuppressWarnings("unchecked")public E next() {//检查此时集合有没有被修改(添加/删除)checkForComodification();int i = cursor;//当前光标的值超过集合总元素个数,抛出不存在此元素异常if (i >= size)throw new NoSuchElementException();//获取集合存储数据的数组Object[] elementData = ArrayList.this.elementData;//当前光标的值超过数组长度,抛出并发修改异常if (i >= elementData.length)throw new ConcurrentModificationException();//移动光标位置,指向下一个元素cursor = i + 1;//返回光标移动前位置的元素,并将其索引赋值给lastRetreturn (E) elementData[lastRet = i];}final void checkForComodification() {//当前集合的被操作次数与创建迭代器时不一致,抛出并发修改异常if (modCount != expectedModCount)throw new ConcurrentModificationException();}}

HashSet底层原理

创建HashSet集合时,创建一个默认长度16,默认加载因子为0.75的数组,数组名为table

添加元素时,根据元素的哈希值跟数组长度计算出应存入位置的索引值

若该位置为null,直接将元素插入进去,若不为null,比较当前元素与该位置元素是否相等,相等则不插入

JDK8以前,不相等的情况 使用头插法,将新元素插入到数组中,旧元素挂在新元素下面,形成链表

JDK8以后,不相等时使用尾插法,即直接将新元素挂在旧元素下面,形成链表

且在JDK8以后,当链表长度超过8并且数组长度大于等于64时,将链表转换为红黑树

若集合中存储的时自定义对象,必须要重写hashCode()和equals()方法

当数组中的元素个数达到数组长度的75%(加载因子)时,数组将扩容为原来的2倍

HashMap源码

创建HashMap对象

//HashMap的三个重要成员变量
//数组默认初始长度
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//数组最大长度
static final int MAXIMUM_CAPACITY = 1 << 30;
//数组默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;//无参构造
public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

添加元素

考虑三种情况

  1. 数组位置为null
  2. 数组位置不为null,键不重复,挂在数组下面形成链表或红黑树
  3. 数组位置不为null,键重复,覆盖原来的值
//put方法传入当前添加元素的key和value
/*
调用putval()方法
第一个参数:调用hash()方法,hash方法使用hashCode()方法得到了key对应的hash值,并进行了一定的处理
简单理解:第一个参数就是根据key获取的hash值
第二个参数:key
第三个参数:value
第四个参数:key相同时是否覆盖,true表示不覆盖,false表示覆盖
第五个参数:不相关,不做解释
*/
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}static final int hash(Object key) {int h;//h = key.hashCode()计算key的hash值return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}//核心方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//定义一个局部变量,用于记录hash表中table的地址值Node<K,V>[] tab;//第三方变量,用于记录集合中元素的地址值Node<K,V> p;//n用于记录数组长度,i用于记录索引int n, i;//第一次添加元素时,table为nullif ((tab = table) == null || (n = tab.length) == 0)//resize方法创建一个默认长度为16,加载因子为0.75的数组赋值给tab,并将数组长度赋值给nn = (tab = resize()).length;/*使用当前插入元素的key和数组长度减一做与运算得到应该插入的位置索引,并获取该位置的元素赋值给p,判断p是否为空,即判断要插入的位置是否为空*/if ((p = tab[i = (n - 1) & hash]) == null)//要插入的位置为空,创建一个新节点并插入tab[i] = newNode(hash, key, value, null);else {//要插入的位置不为空Node<K,V> e;K k;/*判断该位置元素的hash值与新元素的hash值是否相等,若相等再次判断该位置的元素与新元素的key是否相等,若也相等,将该位置元素交由e保存,留待进行值覆盖操作*/if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;/*判断获取到的数组元素是否是红黑树中的节点,若是,则调用putTreeVal方法将新元素按照红黑树规则添加到树当中去*/else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {//获取到的数组元素是链表节点,使用binCount记录数组中的元素个数for (int binCount = 0; ; ++binCount) {/*获取p所指向节点的下一个节点,赋值给e(第一次判断时,p代表的是从数组中获取到的元素,即判断该元素下面是否还有节点),判断是否为空*/if ((e = p.next) == null) {//为空,则创建一个新的节点挂在下面p.next = newNode(hash, key, value, null);//判断链表长度是否超过8,超过则调用treeifyBin方法//treeifyBin方法底层还会继续判断数组长度是否大于等于64//如果两个条件同时满足,就会将数组转为红黑树if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);//跳出循环break;}//不为空,判断e与要插入的节点hash值是否相同,key是否相同if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))//相同,跳出循环,留待进行值覆盖操作break;//不相同,将e赋值给p,继续往下遍历p = e;}}//判断e是否为空,若为空,表示本次插入没有元素被覆盖,若不为空说明需要做覆盖操作if (e != null) { //记录e的存储的值V oldValue = e.value;//判断是否做覆盖操作if (!onlyIfAbsent || oldValue == null)//将新元素的值赋值给ee.value = value;//不相关,不做解释afterNodeAccess(e);//返回被覆盖的值return oldValue;}}++modCount;//判断数组中的元素是否达到了扩容阈值,未达到阈值,不做操作if (++size > threshold)//达到扩容阈值(16*0.75=12),resize方法会将数组扩容为原来的两倍,并将旧数组中的元素全部复制到新数组中去resize();//不相关,不做解释afterNodeInsertion(evict);//返回null表示没有元素被覆盖return null;
}

TreeMap源码

基本属性与构造器

	//比较器private final Comparator<? super K> comparator;//根节点private transient Entry<K,V> root;//节点个数private transient int size = 0;//空参构造public TreeMap() {comparator = null;}//有参构造,传入构造器赋值public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}//使用静态常量表示节点颜色,增加代码可读性private static final boolean RED   = false;private static final boolean BLACK = true;//静态内部类,Entry,集合中的元素都以Entry的类型存储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;...}

添加元素

public V put(K key, V value) {//记录根节点Entry<K,V> t = root;//判断根节点是否为空if (t == null) {compare(key, key); //为空,将插入的节点作为根节点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;//比较新插入元素的key和根元素的key,将差值赋值给cmpcmp = cpr.compare(key, t.key);if (cmp < 0)//cmp小于0,说明新元素要放在t节点的左边,故将t节点的左节点赋值给tt = t.left;else if (cmp > 0)//cmp大于0,说明新元素要放在t节点的右边,故将t节点的右节点赋值给tt = t.right;else//cmp等于0,说明新元素与t节点键相同,做值覆盖操作return t.setValue(value);} while (t != null);}else {//集合未被指定比较器,使用默认比较规则//如果传入的key为null,抛出空指针异常if (key == null)throw new NullPointerException();//压制未检查警告@SuppressWarnings("unchecked")//将新元素的key强转为Comparable类型,要求key必须实现了Comparable接口,否则报错Comparable<? super K> k = (Comparable<? super K>) key;do {//下列代码与上面相同,不做解释parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}//遍历完红黑树,没有覆盖元素,做插入操作,创建新节点Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;//从新插入节点开始调整红黑树fixAfterInsertion(e);size++;modCount++;return null;
}private void fixAfterInsertion(Entry<K,V> x) {//将当前节点颜色设为红色,红黑树节点默认为红色x.color = RED;//按照红黑树规则调整节点//判断当前节点是否为根节点,父节点是否为红色,若父节点为黑色则不需要调整while (x != null && x != root && x.parent.color == RED) {//判断当前节点的父节点是否为爷爷节点的左节点if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//当前节点的父节点是爷爷节点的左节点//获取当前节点的叔叔节点,即当前节点爷爷节点的右节点Entry<K,V> y = rightOf(parentOf(parentOf(x)));//判断叔叔节点的是否为红色if (colorOf(y) == RED) {//叔叔节点为红色//将父节点设为黑色setColor(parentOf(x), BLACK);//将叔叔节点设为黑色setColor(y, BLACK);//将爷爷节点设为红色setColor(parentOf(parentOf(x)), RED);//将爷爷节点赋值给当前节点x = parentOf(parentOf(x));} else {//叔叔节点为黑色//判断当前节点是否为父节点的右节点if (x == rightOf(parentOf(x))) {//当前节点是父节点的右节点//将父节点作为当前节点x = parentOf(x);//以父节点为当前节点进行左旋rotateLeft(x);}//将父节点设为黑色setColor(parentOf(x), BLACK);//将爷爷节点设为红色setColor(parentOf(parentOf(x)), RED);//以爷爷节点为当前节点右旋rotateRight(parentOf(parentOf(x)));}} else {//当前节点的父节点是爷爷节点的右节点,以下代码与上面相同,不再解释Entry<K,V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == leftOf(parentOf(x))) {x = parentOf(x);rotateRight(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateLeft(parentOf(parentOf(x)));}}}//将根节点颜色设为黑色root.color = BLACK;
}

http://www.ppmy.cn/news/46263.html

相关文章

DPDK系列之十三虚拟化virtio源码分析之应用层

一、应用 其实不管怎么设计&#xff0c;如何开发&#xff0c;结果都是要展现一个结果&#xff0c;能够为人所用。虽然说virtio的应用场景有不少&#xff0c;但是在DPDK中主要就是网卡。所以&#xff0c;在此处主要是对网卡的抽象的实现&#xff0c;即对上层的应用实现底层的vi…

【ChatGPT】基于tensorflow2实现transformer(GPT-3.5)

请记住&#xff0c;您是一位NLP领域的专家和优秀的算法工程师。使用带有 tensorflow2.0 subclass api 的 python 从头开始实现 transformer 模型。 全部内容如下&#xff1a; 构建transformer模型架构和依赖层&#xff1b;生成并预处理一些假样本数据&#xff0c;用于训练上面…

NOIP模拟赛 T3区间

题目大意 有 n n n个数字&#xff0c;第 i i i个数字为 a i a_i ai​。有 m m m次询问&#xff0c;每次给出 k i k_i ki​个区间&#xff0c;每个区间表示第 l i , j l_{i,j} li,j​到第 r i , j r_{i,j} ri,j​个数字&#xff0c;求这些区间中一共出现了多少种不同的数字。部…

用Abp实现找回密码和密码强制过期策略

文章目录 重置密码找回密码发送验证码校验验证码发送重置密码链接创建接口 密码强制过期策略改写接口 Vue网页端开发重置密码页面忘记密码控件密码过期提示 项目地址 用户找回密码&#xff0c;确切地说是 重置密码&#xff0c;为了保证用户账号安全&#xff0c;原始密码将不再…

【通过Cpython3.9源码看看列表到底是咋回事】

列表结构 typedef struct {PyObject_VAR_HEAD/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */PyObject **ob_item;/* ob_item contains space for allocated elements. The number* currently in use is ob_size.* Invariants:* 0 < ob_siz…

ObjectARX中的坐标系以及坐标转换

1 AutoCAD中的坐标系种类 WCS World Coordinate System. The “reference” coordinate system. All other coordinate systems are defined relative to the WCS, which never changes. Values measured relative to the WCS are stable across changes to other coordinate s…

MySQL数据库——MySQL数据类型的选择

MySQL 提供了大量的数据类型&#xff0c;为了优化存储和提高数据库性能&#xff0c;在任何情况下都应该使用最精确的数据类型。 前面主要对 MySQL 中的数据类型及其基本特性进行了描述&#xff0c;包括它们能够存放的值的类型和占用空间等。本节主要讨论创建数据库表时如何选择…

Hive常用命令

1.hive模糊搜索表 show tables like *name*; ANALYZE TABLE tablename [PARTITION(partcol1[val1], partcol2[val2], ...)] COMPUTE STATISTICS [noscan]; 2.查看表结构信息 desc formatted table_name;desc table_name; 3.查看分区信息 show partitions table_name; 4.根…