当前位置: 代码网 > it编程>编程语言>Java > Java集合框架详解:HashMap底层原理与扩容机制是什么

Java集合框架详解:HashMap底层原理与扩容机制是什么

2026年09月04日 Java 我要评论
一、java集合1、概述集合、数组都是对多个数据进行存储操作的结构,简称 java 容器。内存层面的存储,不涉及持久化存储。使用 array 存储对象方面具有一些弊端,而 java 集合就像一种容器,

一、java集合

1、概述

集合、数组都是对多个数据进行存储操作的结构,简称 java 容器。内存层面的存储,不涉及持久化存储。

使用 array 存储对象方面具有一些弊端,而 java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。

java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的 关联数组。

数组在内存存储方面的特点:

  • 数组初始化以后,长度就确定了。
  • 数组声明的类型,就决定了进行元素初始化时的类型。

数组在存储数据方面的弊端:

  • 数组初始化以后,长度就不可变了,不便于扩展。
  • 数组中提供的属性和方法有限,不便于进行添加、删除、插入等操作,且效率不高。
  • 数组没有现成的属性或方法获取数组中实际元素的个数。
  • 初始化一个数组的时候,jvm 会在内存上分配一块连续的内存空间,每一个内存空间存一个元素,从首地址开始连续存放,所以数组是有序的,并且存储的数据可以重复的。

java 集合在开发中的应用:

在持久化层通过 sql 语句查询到的多条数据映射到 java 实体类对象,然后通过集合存储多个实体类对象,返回到业务层。

2、集合体系

java 集合可分为 collection 和 map 两种体系:

  • collection 接口:单列集合,用来存储一个一个的对象
  • list:元素有序、有索引、可重复的集合
  • set:元素无序、不可重复、无索引的集合
  • map 接口:双列数据,存储key-value对的数据 

二、collection集合

1、collection接口

collection接口

collection接口常用方法

collection 接口是 list、set 和 queue 接口的父接口,该接口里定义的方法既可用于操作 set 集合,也可用于操作 list 和 queue 集合。

jdk5 之前,java 集合会丢失容器中所有对象的数据类型,把所有对象都作为 object 类型处理。从 jdk5 增加了泛型以后,java 集合可以记住容器中对象的数据类型。

collection是单列集合的父类,它规定的方法(功能)是全部单列集合都会继承的。

常用方法如下:

collection的遍历方式

1、迭代器

迭代器是用来遍历集合的一种方式,在java中迭代器的代表是iterator。

collection集合获取迭代器的方法:

代码实现:

iterator iterator = coll.iterator();
//hasnext():判断是否还下一个元素
while(iterator.hasnext()){
    //next():①指针下移 ②将下移以后集合位置上的元素返回
    system.out.println(iterator.next());
}

原理:

2、增强for循环(foreach循环)

增强for遍历集合,本质就是迭代器遍历集合的简化写法。

增强for循环可以遍历集合和数组

代码格式:

for (元素的数据类型 变量名 : 数组或者集合) {
         
}
collection<string> c = new arraylist<>();
...
for(string s : c) {
    system.out.println(s);
}

快捷方式

单列集合名 或者 数组名.for。

3、foreach(lambda表达式)

因为collection接口中包含set集合子接口,set子接口无索引,所以不能用for循环遍历。

2、list接口

  • list系列集合特点:   有序,可重复,有索引  
  • arraylist:有序,可重复,有索引。  
  • linkedlist:有序,可重复,有索引。

list特有方法

list集合因为支持索引,所以多了很多与索引相关的方法,当然,collection的功能list也都继承了。

遍历方式

  • for循环(因为list集合有索引)
  • 迭代器
  • 增强for循环
  • foreach(lambda表达式)

arraylist集合的底层原理

arraylist基于数组实现的。

  • 查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
  • 删除效率低:可能需要把后面很多的数据进行前移。
  • 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。

应用场景:

linkedlist集合的底层原理

linkedlist是基于双链表实现的。

特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。

linkedlist新增了:很多首尾操作的特有方法

linkedlist的应用场景之一:可以用来设计队列

linkedlist的应用场景之一:可以用来设计栈

push、pop方法底层还是使用linkedlist的首尾操作方法;

3、set接口

  • set系列集合特点:无序:添加数据的顺序和获取出的数据顺序不一致;  不重复; 无索引;
  • hashset : 无序、不重复、无索引。
  • linkedhashset:有序、不重复、无索引。
  • treeset:排序、不重复、无索引。

set要用到的常用方法,基本上就是collection提供的!!

hashset集合的底层原理

前置知识:哈希值(hash值)

就是一个int类型的数值,java中每个对象都有一个哈希值。

java中的所有对象,都可以调用obejct类提供的hashcode方法,返回该对象自己的哈希值。

public int hashcode():返回对象的哈希码值。 

对象哈希值的特点:

  • 同一个对象多次调用hashcode()方法返回的哈希值是相同的。
  • 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。

hashset的底层原理:

  • 基于哈希表实现。
  • 哈希表是一种增删改查数据,性能都较好的数据结构。

哈希表

  • jdk8之前,哈希表 = 数组+链表
  • jdk8开始,哈希表 = 数组+链表+红黑树

深入理解hashset集合去重复的机制:

hashset集合默认不能对内容一样的两个不同对象去重复! 

比如内容一样的两个学生对象存入到hashset集合中去 , hashset集合是不能去重复的!

如果希望set集合认为2个内容一样的对象是重复的, 必须重写对象的hashcode()和equals()方法。

linkedhashset底层原理

依然是基于哈希表(数组、链表、红黑树)实现的。

但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。

treeset底层原理

特点:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序) 底层是基于红黑树实现的排序。

需要注意的是:

  • 对于数值类型:integer , double,默认按照数值本身的大小进行升序排序。
  • 对于字符串类型:默认按照首字符的编号升序排序。

对于自定义类型如student对象,treeset默认是无法直接排序的。

treeset集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。

  • 方式一  让自定义的类(如学生类)实现comparable接口,重写里面的compareto方法来指定比较规则。
  • 方式二 通过调用treeset集合有参数构造器,可以设置comparator对象(比较器对象,用于指定比较规则。

两种方式中,关于返回值的规则:

  • 如果认为第一个元素 >  第二个元素 返回正整数即可。 如果认为第一个元素 < 第二个元素返回负整数即可。
  • 如果认为第一个元素 = 第二个元素返回0即可,此时treeset集合只会保留一个元素,认为两者重复。

注意:如果类本身有实现comparable接口,treeset集合同时也自带比较器,默认使用集合自带的比较器排序

4、collection集合总结

1、场景选择

2、集合并发修改异常

使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。

由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误

怎么保证遍历集合同时删除数据时不出bug?

使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可。

如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。

5、collections工具类

collections是java提供的类,专门用于操作集合 由于构造被私有, 方法都是静态, 所以被称之为工具类

collections提供的常用静态方法:

三、map集合

1、概述

特点

map集合称为双列集合,格式:{key1=value1 , key2=value2 , key3=value3 , ...}, 一次需要存一对数据做为一个元素.

map集合的每个元素“key=value”称为一个键值对/键值对对象/一个entry对象,map集合也被叫做“键值对集合”

map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值

map集合体系特点

注意:map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的

hashmap(由键决定特点): 无序、不重复、无索引;  (用的最多)

linkedhashmap (由键决定特点):有序、不重复、无索引;

treemap (由键决定特点):按照大小默认升序排序、不重复、无索引。

常用方法

map是双列集合的父类,它的功能是全部双列集合都可以继承过来使用的。

map的常用方法如下:

遍历方式

1、键找值

先获取map集合全部的键,再通过遍历键来找值

需要用到map的如下方法:

获取键的集合后,可通过循环获取键对应的值。

2、键值对

3、lambda

需要用到map的如下方法:

内部使用entryset()。

map.foreach((k,v))->{
    system.out.println(k+"--->"+v);
}

2、hashmap底层原理及应用场景

hashmap的数据结构: 底层使用hash表数据结构,即数组和链表的结合体。

hashmap 基于 hash 算法实现的

1. 当我们往hashmap中put元素时,利用key的hashcode重新hash计算出当前对象的元素在数 组中的下标

2. 存储时,如果出现hash值相同的key,此时有两种情况。

  • 如果key相同,则覆盖原始值;
  • 如果key不同(出现冲突),则将当前的key-value放入链表中

3. 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。

hashmap jdk1.8之前

jdk1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链 表中即可。

hashmap jdk1.8之后

相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8) 时,将链表转化为红黑树,以减少搜索时间。

扩容 resize( ) 时,红黑树拆分成的树的结点数小于等于临界值6个,则退化成链表。

hashmap的put方法的具体流程

文字描述:

  • 1. 判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;
  • 2. 根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向 ⑥,如果table[i]不为空,转向③;
  • 3. 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的 是hashcode以及equals;
  • 4. 判断table[i] 是否为treenode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值 对,否则转向⑤
  • 5. 遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操 作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;
  • 6. 插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩 容。

hashmap的扩容机制

  • 1. 在jdk1.8中,resize方法是在hashmap中的键值对大于阀值(0.75)时或者初始化时,就调用resize方法进 行扩容;
  • 2. 每次扩展的时候,都是扩展2倍;
  • 3. 扩展后node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。

3、linkedhashmap集合的原理

底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。

实际上:linkedhashset集合的底层原理就是linkedhashmap。

4、treemap集合的原理

特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)

原理:treemap跟treeset集合的底层原理是一样的,都是基于红黑树实现的排序。

treemap集合同样也支持两种方式来指定排序规则

让类实现comparable接口,重写比较规则。

treemap集合有一个有参数构造器,支持创建comparator比较器对象,以便用来指定比较规则。

集合的嵌套

homie可自行学习,比较简单,主要是集合中的类型选择

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com