面试查漏补缺--java基础-容器源码解读

news2024/11/17 9:46:56

前言:
本文主要是通过源码来解读一些自己还不懂的地方,一些数据结构上的东西,不做过多的解读。


文章目录

    • 一、容器体系
    • 二、List容器
      • 2.1 ArrayList源码
      • 2.2 Vector 源码
      • 2.3 LinkedList
    • 三、Set容器
      • 3.1 HashSet


一、容器体系

容器总的来说分为两大类:
1、Collection:
存放的是单建元素(就是单个元素)

2、Map<K,V>:
存放的键值对(以Key-Value的方式存在)

  • 1、Collection下的两大接口,List 和 Set
    在这里插入图片描述
  • Map下的三大容器(都是以建值对的方式向Map中存放值)
    在这里插入图片描述

二、List容器

List定义的模板方法如下:
在这里插入图片描述

2.1 ArrayList源码

底层的数据结构是数组,所以使用idx
在这里插入图片描述

学习重点在于扩容(add方法中很重要):

// 测试代码
public class ListTest {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        list.add(20000);
    }
}

1、使用默认的构造函数
在这里插入图片描述

2、调用add方法

  • (1)先确认是否需要扩容
    在这里插入图片描述
    -(2)这就是默认容量为10的代码
    在这里插入图片描述
    -(3)明确容量(判断是否扩容)
    1、modCount这是一个全局变量,每次添加值都会对其++,
    2、判断现有的数组长度是否已经不能容下新元素了
    3、在我的测试代码中,前十个是不会扩容的,到第11个才会进入if
    在这里插入图片描述
    -(4)扩容
    将原来的数组扩容原来的1.5倍得到一个新的数组,将原来的值copy到新数组。
    在这里插入图片描述

2.2 Vector 源码

其实看了源码可以知道:
Vector底层也一个数组,基本上和ArrayList一直

通过看源码,我们可以知道Vector是通过对每个方法添加synchronized关键字来保证这是一个线程安全的容器。

那么这样会使我们再使用这些方法时效率不高。

所以我们再日常开发中,很少使用他,但是再有竞争的情况下可以使用一下【其实使用也不是特别多,我们都是通过自己去完成的多】

在这里插入图片描述

2.3 LinkedList

1、底层是一个双向链表的数据结构

源码是定义了一个node节点,来存放前驱、后驱和元素

他的CRUD也是比较简单,主要就是基于双向链表的CRUD,要考虑好前驱指针和后驱指针指向的地址,就能弄明白。
在这里插入图片描述
在这里插入图片描述

  • List下ArrayList和LinkedList的比较

在这里插入图片描述

三、Set容器

  • 简介:
    不可重复,无序(其实是按运算出来的hash值排序的【这里不是hashCode】)

3.1 HashSet

  • HashSet简介:
    他的底层是由HashMap完成的,就是等价于HashMap

总体add流程如下图:
在这里插入图片描述

  • 测试代码
HashSet<Object> objects1 = new HashSet<>();
        for (int i = 0; i < 2; i++) {
            objects1.add(i);
        }
        // 重复(看比较)
        objects1.add(2);
  • 源码解读

1)构造方法
构造方法也很清楚就是,hashMap
在这里插入图片描述
2)add方法

  • 2.1 向hashMap中put数据,key就是我们add进来的值,Value是共享的一个填充对象
    在这里插入图片描述
    填充对像
    在这里插入图片描述
    ===========>这里往下就是HashMap的东西咯,小东西真面目流露了

  • 2.2 再往下面调用Map中的put方法了
    在这里插入图片描述

  • 2.3 将hashCode进行运算

将hashcode进行高16位和低16位进行异或运算(这样做的目的就是尽量的避免哈希冲突)==》得到hash值
在这里插入图片描述

  • 2.4 put的核心代码注释
    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
         // 临时辅助遍历
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 当定义的属性,数组位null 或者长队为0时调用resize方法进行长度的重置(下面的第二段代码注释)
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
         // 使用n-1&hash计算下标值(数组长度&hash)
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

// 注意:阈值

    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    final Node<K,V>[] resize() {
    	// 定义老的数组
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
        // 必须是二次幂
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
        // 这里是初次使用的默认值是16
            newCap = DEFAULT_INITIAL_CAPACITY;
            // 这是给扩容值进行计算(阈值是0.75*默认长度)
            //【这里之所以使用阈值计算扩容值,是为了防止并情况下扩容的问题假设你到满了才扩容,比如还剩4个位置,刚好同时来了5个,那怎么办呢】
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

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

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

相关文章

小小博客项目(servlet实战演练)

目录 MVC模式简介 项目概述 &#x1f351;Model&#xff08;模型层&#xff09; &#x1f351;View&#xff08;视图层&#xff09; &#x1f351;Controller&#xff08;控制器层) 项目实战 上面pom.xml代码参考 一、模型层 &#x1f330;User代码&#xff1a;对应数据…

什么是NFS?NFS挂载

文章目录1、NFS服务2、RPC服务与NFS3、NFS的优缺点4、NFS服务端的搭建与配置5、小结1、NFS服务 NFS&#xff0c;全称Network File System&#xff0c;即网络文件系统。最大的功能是通过网络&#xff0c;让不同的机器、不同的操作系统可以共享彼此的文件。 &#x1f609; 更直白…

Go语言开发k8s-05-ConfigMap操作

文章目录1. 结构体1.1 ConfigMapList1.2 ConfigMap1.3 TypeMeta1.4 ObjectMeta1.7 对照yml文件示例1.5 Immutable1.6 Data1.7 BinaryData2. Create configMap语法完整示例3. Get ConfigMapList语法完整示例4. Get ConfigMap语法完整示例5. Update ConfigMap语法完整示例6. Dele…

【网络篇】第二篇——IP协议与MAC地址详解

IP协议 理解源IP地址和目的IP地址 网段划分 IP地址的数量限制 私有IP地址和公网IP地址 路由 NAT(网络地址转换) ​编辑NAT IP转换过程 NAPT MAC地址 理解源MAC地址和目的MAC地址 对比理解MAC地址和IP地址 只有一个MAC地址&#xff0c;可以传输数据嘛? 为什么有了…

网络原理——网络层与数据链路层

JavaEE传送门JavaEE 网络原理——No.3 传输层_TCP的滑动窗口, 流量控制与拥塞控制 网络原理——No.4 传输层_TCP协议中的延迟应答, 捎带应答, 面向字节流与TCP的异常处理 目录网络层IP 协议IP 地址路由选择数据链路层以太网网络层 网络层做的工作, 就是两点之间, 规划出一个合…

python基于PHP+MySQL的健身房管理系统

随着时代的发展人们人们对身体不健康越来越重视。身体是革命的本钱,所以只有有一个好的身体才能够积极地参加到工作和学习中去。当前社会生活节奏较快人们往往没有时间进行锻炼于是造成了很多富贵病以及办公室职业病的发生。这是一种极其不健康的生活方式,我为了能够让这些亚健…

坚持记账的5大好处,你知道吗?

记账可以理清生活开支&#xff0c;有效节流通过记账&#xff0c;每时每刻都能知道自己的财务状况&#xff0c;花钱的时候会更有目的与计划&#xff0c;不像以前手忙脚乱一团糟&#xff0c;也不再寅吃卯粮&#xff0c;随意透支。对于很多不知道钱花到哪里去的人来说&#xff0c;…

SpringBoot(自定义注解)

目录 1.注解的概念 1.1 什么是注解 1.2 java注解分类 1.3 JDK基本注解 1.4 JDK元注解 2.自定义注解 2.1 自定义注解的组成 2.2 如何自定义注解 2.3 案例 2.3.1 简单了解基本属性 2.3.2 获取类与方法上的注解值 2.3.3 获取类属性上的注解属性值 2.3.4 获取参数修饰注解对应的属…

MQTT X v1.8.3 正式发布

近日&#xff0c;MQTT X 发布了最新的 1.8.3 版本&#xff0c;主要对功能使用进行了优化&#xff0c;并修复了使用过程中所出现的各类问题。例如&#xff0c;优化了 MQTT 5.0 Clean Start 的使用方式&#xff0c;为会话过期间隔添加默认值&#xff1b;优化 MQTT X CLI 的默认输…

数据结构与算法分析之并查集

1. 并查集 并查集是一种树型的数据结构&#xff0c;并查集可以高效的进行如下操作&#xff1a; 查询元素p和元素q是否属于同一组合并元素p和元素q所在的组 1.1 并查集结构 并查集是一种树型结构&#xff0c;但这棵树和我们之前学的二叉树、红黑树、B树等都不一样&#xff…

WindowsServer2016配置故障转移群集图文教程

准备工作 首先准备好两台以上的服务器&#xff0c;并记录好IP和主机名。如下 172.31.210.203 WIN-S8LC9RKL4BB 172.31.215.54 WIN-76A6N72MRTD 之后需要配置到host文件中。 正文 基础配置 首先打开host文件&#xff0c;把所有的IP及主机添加进去。host文件地址如下。 C:\Wi…

10.30-11.3|浙大报考点硕士研究生2023年网上确认系统操作流程

一、登陆《2023年全国硕士研究生招生考试网上确认系统》https://yz.chsi.com.cn/wsqr/stu/index.html。填写考生所在地&#xff0c;点击“确定”。二、点击“开始进行网上确认”。 三、阅读网报公告之后&#xff0c;点击“我已经阅读完毕”。 四、阅读考生诚信考试承诺书&#…

QT5串口编程——编写简单的上位机

下面开始介绍串口类的使用。 首先&#xff0c;QT5是自带QSerialPort这个类的&#xff0c;使用时需要在pro文件里面添加一行&#xff1a; ​然后直接引用头文件就可以了。 ​在QT5中&#xff0c;串口通信是借助一个QSerialPort的对象来实现的&#xff0c;在设置QSerialPort对象…

课堂笔记| 第七章:多态

本节课要点&#xff1a; 继承特性多态虚函数目录 一、多继承 二、继承的前提&#xff1a;正确的分类 三、多态 1. 虚函数 2. 确保覆盖和终止覆盖 3. 虚函数的实现原理 4. 虚析构函数 四、纯虚函数和抽象类 1. 纯虚函数 2. 抽象类 一、多继承 在之前的课程中&a…

SpringCloud Alibaba Sentinel实现熔断与限流

目录 一、简介 1.官网 & 介绍 2.下载地址 3.作用 4.如何使用 ⭐解决服务使用中的各种问题 5.Sentinel与Hystrix的区别 二、安装sentinel控制台 1.sentinel组件由2部分构成 2.安装步骤 ①地址 ②运行命令 ③访问sentinel界面 三、初始化演示工程 1.启动naco…

MyBatis 环境搭建配置全过程【IDEA】

文章目录一、MyBatis 介绍二、MyBatis 环境搭建1.MyBatis 下载2.配置 jdk 版本3.创建 Maven 工程4.IDEA 连接数据库5.项目文件构架6.引入相关依赖7.命令行创建数据库8.数据库配置文件9.核心配置文件三、入门测试程序1.创建表准备数据2.创建 POJO 实体3.创建映射文件4.修改核心配…

如何增加 KVM 虚拟机的磁盘大小

KVM 是一种集成到 Linux 内核中的虚拟化技术。您可以使用virsh、virt-manager和GNOME Boxes等工具创建虚拟机并与 KVM 交互。 磁盘空间不足是最常见的 VM 来宾问题之一。在测试新 VM 时,您可能会故意使用较小的磁盘。随着时间的推移,您会累积文件,直到虚拟磁盘几乎已满。以…

C语言笔记

fabs用来求double类型的绝对值&#xff0c;小数点后保留6位#include<math.h> double fabs(double ) labs用来求长整型long整型的绝对值&#xff0c; long cabs(long n); abs用来求整数的绝对值&#xff0c;labs求long long的绝对值#include<stdlib.h> double ret …

初识C++ (二)

初识C 二 上节课输入输出问题的一些补充一. 缺省参数1.1 半缺省参数1.2 全缺省参数二. 函数重载2.1 重载是什么意思&#xff1f;2.2 如何区分重载函数参数类型不同参数个数不同参数顺序不同附加题1附加题22.3 c支持函数重载的原理预处理编译汇编连接总结要以一种很认真的态度去…

深度优先搜索(dfs)和广度优先搜索(bfs)

目录 一、前言 二、关于dfs和bfs有意思的小故事 三、深搜题例 1、小猫爬山链接 2、基本思路 3、代码 &#xff08;1&#xff09;python代码 四、广搜题例 1、武士风度的牛链接 2、基本思路 3、代码 &#xff08;1&#xff09;C代码 &#xff08;3&#xff09;pyth…