ArrayList 和 HashMap 源码解析

news2024/9/21 20:38:59

1、ArrayList

1.1、ArrayList 构造方法

无参创建一个 ArrayList 数组默认为空数组

transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private int size; // 数组容量大小

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

指定数组大小 创建一个 ArrayList 数组默认为 指定大小,
如果 initialCapacity == 0 默认为空数组
如果 initialCapacity < 0 则会抛出异常

transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

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);
        }
    }

1.2、add 方法

1、判断数组是否为空,为空则扩容为10,然后修改次数加1,添加数据到数组
2、不为空,则获取扩容大小(原数组大小 + 原数组右移1位),相当于扩容为原来的1.5倍,判断扩容后若小于当前需要的容量,则扩容为当前需要容量大小,然后修改次数加1,添加数据到数组
3、不为空,则扩容(原数组大小 + 原数组右移1位),相当于扩容为原来的1.5倍,判断扩容后若不小于当前需要的容量,则1.5倍扩容,然后修改次数加1,添加数据到数组
4、第四重则是判断是否超过 Integer.MAX_VALUE,2的31次方-1.若是则抛出异常 OutOfMemoryError

  • 扩容机制:默认大小为0,在新增的时候才会扩容,正常情况默认扩容为原来的1.5倍,然后会重新创建一个1.5倍大小的数组,再把原来数组上面的数据复制到新数组上面。

第二种情况只有设置数组初始容量为1时,才会发生。因为 1 >> 1 = 0;这时扩容时才会出现扩容后的比需要的小,才会进入 if (newCapacity - minCapacity < 0) 中

	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // size 是当前数组大小,目前是0,传入1
        elementData[size++] = e;
        return true;
    }

	private void ensureCapacityInternal(int minCapacity) { // 进入这个方法
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 判断当前数组是否为空
            return Math.max(DEFAULT_CAPACITY, minCapacity); // 为空则返回一个大的值,10 和 1,10比较大,所以返回10
        }
        return minCapacity;
    }
    
	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    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);
    }

总结:
ArrayList 底层基于数组实现,查询快O(1),新增修改慢O(n),
查询基于索引,新增删除可能涉及到索引移位,新增还会涉及扩容
数组的元素都是连续存储的,这意味着在内存中,数组的元素之间是相邻的

2、LinkedList

LinkedList 是一个双向链表结构,每个节点是一个 node 节点,如下:
item 中存储的是数据,next存储的是下一个node节点的地址,prev存储的是上一个node节点的地址

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 提供了针对头尾节点的操作,使得对头尾节点的 增删查 较快
在这里插入图片描述
总结:

LinkedList 底层基于双向链表结构,增删快O(1),查询慢O(n)。
链表中的节点并不一定是在内存中连续存放的。每个节点实际上是一个对象,而对象在内存中是根据可用的空间分配的,因此它们不一定是连续的。LinkedList 中的节点通过引用(指针)来连接,而这些节点在内存中的位置是不连续的。

应用场景:

因为 removeFirst 会返回删除的元素,所以可以模拟 FIFO(先进先出)

1、可以用于设计队列
2、可以用于设计栈

3、HashMap

  1. 初始化的时候设置大小,因为扩容、重新计算hash值非常费时,建议为2的n次方。如果没有设置初始值默认为16(首次put时才会创建16的数组)
  2. jdk1.8以前是数组+单向链表,jdk1.8以后是数组+单向链表+红黑树
  3. HashMap 不是线程安全的,涉及到线程安全建议使用 ConcurrentHashMap
  4. 当链表长度等于8时,如果容量没有达到64,会先扩容。如果容量达到了64,链表会转为红黑树。当红黑树节点小于6个时会转为链表
  5. 数组为Node类型,在jdk7中称为Entry类型
  6. 链表时间复杂度为O(n),红黑树为O(logn)

3.1、HashMap 扩容机制

扩容是为了减少哈希冲突,提高存取效率。
HashMap 负载因子默认为 0.75

  1. 当临界值 > HashMap数组长度 * 0.75 就会进行扩容
  2. 当链表长度为8,HashMap数组长度小于64,就会进行扩容

hashmap扩容时会创建一个原数组两倍大的数组,然后遍历原数组,将原来所有的数据都移到新数组中。扩容之后需要重新hash,因为哈希值 = hashcode % (length -1),扩容后长度改变,hash值也会随之改变。

3.2、Node节点

哈希值(hash)、键(key)、值(value)以及指向下一个节点的引用(next)。
在 HashMap 的实现中,存储的键值对数据结构是通过链表来实现的。每个 Node 对象就是链表中的一个节点,通过 next 引用指向下一个节点。这样就可以处理哈希冲突,因为具有相同哈希值的键值对会放在同一个哈希桶中,通过链表进行连接。当链表长度达到一定阈值时,会将链表转换为红黑树以提高查询效率。

Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

3.3、负载因子

为什么负载因子是0.75呢?

参数的设置是经过实际测试和经验得出的。加载因子的选择需要权衡空间和时间效率,
过小的加载因子会导致哈希表频繁扩容,
而过大的加载因子会导致链表过长,查找效率下降。
因此,0.75 是一个比较合理的折衷选择,可以在空间和时间上实现比较好的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1264700.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

别太担心,人类只是把一小部分理性和感性放到了AI里

尽管人工智能&#xff08;AI&#xff09;在许多方面已经取得了重大进展&#xff0c;但它仍然无法完全复制人类的理性和感性。AI目前主要侧重于处理逻辑和分析任务&#xff0c;而人类则具有更复杂的思维能力和情感经验。 人类已经成功地将一些可以数据化和程序化的理性和感性特征…

传智杯-题目1

运气 一&#xff1a;对于每一的1到6都进行枚举&#xff0c;进行递归操作 二&#xff1a;如果位数到了指定的n的时候&#xff0c;递归的条件&#xff0c;进行判断是否可以整除操作 #include<iostream> #include<algorithm> using namespace std; long long n, k, an…

你要的fiddler快捷键全部在这里了,学最全的快捷键,做最快的IT程序员

一、常用三个快捷键 ctrlX :清空所有记录 CtrlF&#xff1a;查找 F12&#xff1a;启动或者停止抓包 使用 QuickExec Fiddler2 成了网页调试必备的工具&#xff0c;抓包看数据。Fiddler2自带命令行控制。 fiddler 命令行快捷键&#xff1a;ctrl q &#xff0c;然后 输入 help…

JVM运行时数据区域、对象内存分配、内存溢出异常总结

深入理解java虚拟机第二章 二、运行时数据区域2.2.1 程序计数器2.2.2 Java虚拟机栈2.2.3 本地方法栈2.2.4 Java堆2.2.5 方法区2.2.6 运行时常量池2.2.7 直接内存 三、HotSpot虚拟机对象解密2.3.1 对象的创建对象如何分配内存&#xff1f;对象的创建如何处理并发问题&#xff1f…

Flask教程入门

1.学习Flask之前&#xff0c;首先需要对URL进行一定的了解。 URL的一些知识&#xff1a; 1.URL只能包含ASCII码里面一些可显示的字符&#xff0c;如A-Z&#xff0c;a-z&#xff0c;0-9&#xff0c;&&#xff0c;#&#xff0c;%&#xff0c;&#xff1f;&#xff0c;/等字符…

前端量子纠缠 效果炸裂 multipleWindow3dScene

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; &#x1f383; 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲 ✈️已经旅游的地点 | 新疆-乌鲁木齐、新疆-吐鲁番、广东-广州…

skywalking告警qq邮箱发送

首先开启发送接收qq邮箱的权限 开启之后&#xff0c;会让你发送信息&#xff0c;按着一系列操作&#xff0c;获得password &#xff08;授权码&#xff08;例如&#xff0c;qq开启SMTP授权码&#xff0c;qq授权码16位&#xff09;&#xff09; <!-- mail邮箱-->…

如果每天工资按代码行数来算,来看看你每天工资是多少

说在前面 &#x1f63c;&#x1f63c;如果每天的工资取决于我们所编写的代码行数&#xff0c;那么我们的生活会发生怎样的改变&#xff1f;来看看你的同事们今天都提交了多少代码吧&#xff0c;看看谁是卷王&#xff0c;谁在摸鱼&#xff08;&#x1f436;&#x1f436;狗头保命…

Java之顺序栈实现数制转换问题

一、前言&#xff1a; 这是怀化学院的&#xff1a;Java数据结构中的一道中等编程题(此方法为博主自己研究&#xff0c;问题基本解决&#xff0c;若有bug欢迎下方评论&#xff0c;我会第一时间改进代码&#xff0c;谢谢&#xff01;) 后面其他编程题只要我写完成功&#xff0c;会…

前端---CSS篇(详解CSS)

1.CSS简介 CSS(Cascading Style Sheets)层叠样式表&#xff0c;是用来为结构化文档&#xff08;HTML、XML等应用&#xff09;添加样式,比如字体、颜色、大小、间距的计算机语言。CSS目前已经发展到了CSS3.0了。 2.CSS导入方式 CSS有三种导入方式&#xff1a; 1.行内样式&am…

基于SSM的电商购物网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

类和对象——(2)类

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 只虽然夜晚很长&#xff0c;但天…

鸿蒙4.0开发笔记之ArkTS语法基础@Entry@Component自定义组件的使用(九)

文章目录 一、自定义组件概述1、什么是自定义组件2、自定义组件的优点 二、创建自定义组件1、自定义组件的结构2、自定义组件要点3、成员变量的创建4、参数传递规则 三、练习案例 一、自定义组件概述 1、什么是自定义组件 在ArkUI中&#xff0c;UI显示的内容均为组件&#xf…

CodeTON Round #7 (Div. 1 + Div. 2)

A.jagged Swaps 题意&#xff1a; 给出一个包含 n n n个数字的序列&#xff0c;每次可以选择一个同时大于左右两边相邻的数字&#xff0c;将这个数字与它右边的数字交换&#xff0c;问能否在经过若干次操作后使序列变为升序。 分析&#xff1a; 由于交换只能向后进行&#…

OpenCV | 模版匹配

import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline 模版匹配 模版匹配和卷积原理很像&#xff0c;模版在原图像上从原点开始滑动&#xff0c;计算模版与&#xff08;图像被模版覆盖的地方&#xff…

SAP VL01N Error VL 367

在VL01N 创建交货单的时候&#xff0c;报错&#xff1a;An item with no delivery quantity is not permitted. Item will be deleted. 第一种情况&#xff1a; 1.首先MMBEcheck 可用库存&#xff0c;非限制使用库存是否充足 注意&#xff1a;这里框出来的交货库存也是非限制…

HMM预习中文版

马尔可夫模型 在之前的笔记中&#xff0c;我们讨论了贝叶斯网络&#xff0c;以及它们如何被用于紧凑地表示随机变量之间的关系。现在&#xff0c;我们将介绍一个与之紧密相关的结构&#xff0c;称为马尔可夫模型&#xff0c;对于本课程的目的&#xff0c;可以将其视为类似于链…

位运算算法【1】

文章目录 &#x1f34a;面试题 01.01. 判定字符是否唯一&#x1f96d;题目&#x1f351;算法原理&#x1f95d;解法一&#xff1a;哈希表&#x1f95d;解法二&#xff1a;位图 &#x1f951;代码实现 &#x1f33d;268. 丢失的数字&#x1f96c;题目&#x1f344;算法原理&…

多线程(补充知识)

STL库&#xff0c;智能指针和线程安全 STL中的容器是否是线程安全的? 不是. 原因是, STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全,会对性能造成巨大的影响. 而且对于不同的容器, 加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶). 因此 STL 默认…

(Linux2.6内核)进程调度队列与切换

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 我们首先来了解几个概念 1. 进程在CPU上运行的时候&#xff0c;一定要运行完才行吗&#xff1f;答案是否定的&#xff0c;我们大部分的操作系统&#xff0c;主流就是分时操作系统&#xff0c;即基于时间片进程轮转执行的。 …