Java集合(二)

1. Map

1.1 HashMap 和 Hashtable 的区别

  • 线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
  • 效率: 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点。另外,Hashtable 基本被淘汰,不要在代码中使用它;
  • 对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException
  • 初始容量大小和每次扩充容量大小的不同: ① 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小(HashMap 中的tableSizeFor()方法保证,下面给出了源代码)。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
  • 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树),以减少搜索时间(后文中我会结合源码对这一过程进行分析)。Hashtable 没有这样的机制。

HashMap 中带有初始容量的构造函数:

    public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}

下面这个方法保证了 HashMap 总是使用 2 的幂作为哈希表的大小。

    /*** Returns a power of two size for the given target capacity.*/static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}

 1.2 HashMap 和 HashSet 区别

如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 clone()writeObject()readObject()HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。

方法都是直接调用 HashMap 中的方法。

HashMapHashSet
实现了 Map 接口实现 Set 接口
存储键值对仅存储对象
调用 put()向 map 中添加元素调用 add()方法向 Set 中添加元素
HashMap 使用键(Key)计算 hashcodeHashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性

1.3 HashMap 和 TreeMap 区别

TreeMap 和HashMap 都继承自AbstractMap ,但是需要注意的是TreeMap它还实现了NavigableMap接口和SortedMap 接口。

实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力。

实现SortedMap接口让 TreeMap 有了对集合中的元素根据键排序的能力。默认是按 key 的升序排序,不过我们也可以指定排序的比较器。示例代码如下:

/*** @author shuang.kou* @createTime 2020年06月15日 17:02:00*/
public class Person {private Integer age;public Person(Integer age) {this.age = age;}public Integer getAge() {return age;}public static void main(String[] args) {TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {@Overridepublic int compare(Person person1, Person person2) {int num = person1.getAge() - person2.getAge();return Integer.compare(num, 0);}});treeMap.put(new Person(3), "person1");treeMap.put(new Person(18), "person2");treeMap.put(new Person(35), "person3");treeMap.put(new Person(16), "person4");treeMap.entrySet().stream().forEach(personStringEntry -> {System.out.println(personStringEntry.getValue());});}
}

person1
person4
person2
person3 

可以看出,TreeMap 中的元素已经是按照 Person 的 age 字段的升序来排列了。

上面,我们是通过传入匿名内部类的方式实现的,你可以将代码替换成 Lambda 表达式实现的方式:

TreeMap<Person, String> treeMap = new TreeMap<>((person1, person2) -> {int num = person1.getAge() - person2.getAge();return Integer.compare(num, 0);
});

综上,相比于HashMap来说 TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。

1.4 HashSet 如何检查重复?

当你把对象加入HashSet时,HashSet 会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcodeHashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加入操作成功。 

 在 JDK1.8 中,HashSetadd()方法只是简单的调用了HashMapput()方法,并且判断了一下返回值以确保是否有重复元素。直接看一下HashSet中的源码:

// Returns: true if this set did not already contain the specified element
// 返回值:当 set 中没有包含 add 的元素时返回真
public boolean add(E e) {return map.put(e, PRESENT)==null;
}

而在HashMapputVal()方法中也能看到如下说明:

// Returns : previous value, or null if none
// 返回值:如果插入位置没有元素返回null,否则返回上一个元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
...
}

也就是说,在 JDK1.8 中,实际上无论HashSet中是否已经存在了某元素,HashSet都会直接插入,只是会在add()方法的返回值处告诉我们插入前是否存在相同元素。

1.5 HashMap 的底层实现

1.5.1 JDK1.8 之前

JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 hashcode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。

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

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

相关文章

Yocto - bb脚本中使用的SRC_URI、SRCREV和S

我们遇到的各种自己不了解的技术或产品时&#xff0c;都需要阅读用户手册。用户手册里的内容很多时&#xff0c;除了由目录组织文档结构外&#xff0c;通常还有有一个词汇表&#xff0c;一般作为附录放在文档最后。 通过这个按照字母排序的词汇表&#xff0c;可以在对整个文档还…

C#,数值计算——插值和外推,谢别德(Shep)插值方法的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 谢别德插值方法 /// Object for Shepard interpolation using n points in dim dimensions. Call /// constructor once, then interp as many times as desired. /// &…

前端面试提问(3)

1、js两个数相加会不会丢精度&#xff1f; 可能会遇到精度丢失的问题。JavaScript 使用的是 IEEE 754 浮点数标准&#xff0c;即一种二进制表示法&#xff0c;有时不能准确地表示十进制小数。如果你需要进行精确的十进制数值计算&#xff0c;可以使用一些处理精确数值的库&…

4.C转python

1.建立函数: def 函数名(形参): 函数体(记得写缩进) return 返回值(python中可以没有return) 2.调用函数: 函数名(实参) 实参和形参个数相等即可,类型不需要相同 其中接收返回值与C中的差不多 3.如果只是定义而不调用则函数不会执行 4.先定义函数,后调用 5.python中可以…

每天五分钟计算机视觉:ImageNet大赛的世界冠军AlexNet模型

AlexNet模型 2012 Imagenet 比赛第一&#xff0c;Top5准确度超出第二10% &#xff0c;它让人们认识到了深度学习技术的威力。比 LeNet更深&#xff0c;用多层小卷积层叠加替换大卷积层&#xff0c;就是说每一个卷积层的通道数小&#xff0c;不像LeNet一样每个卷积层的通道数很大…

记录华为云服务器(Linux 可视化 宝塔面板)-- 防火墙篇

文章目录 前言安装防火墙防火墙设置防火墙操作1.设置开机启动防火墙2.查看防火墙开放哪些端口3.重载防火墙配置&#xff08;修改配置后重新启动才生效&#xff09;4.查看防火墙状态5.开启防火墙6.关闭防火墙 若遇到无法开启查询已开放的端口查询端口是否开放&#xff08;80&…

简单了解设计模式

常见的设计模式大致可以分为三个主要类别&#xff1a;创建型、结构型和行为型。以下是这三个类别下的各种模式&#xff1a; 创建型模式 (Creational Patterns)&#xff1a;这类模式主要关注如何创建对象。 单例模式 (Singleton)&#xff1a;确保某一个类只有一个实例&#xff0…

在内网开发中使用Nginx代理来访问钉钉新版服务端API

如果你在内网开发中使用Nginx代理来访问钉钉新版服务端API&#xff0c;你可以在Nginx配置文件中进行相应的配置。 以下是一个简单的示例Nginx配置&#xff0c;用于将对指定URL的请求代理到钉钉服务端API&#xff1a; server { listen 80; server_name your_server_domain; l…

Python爬虫-新能源汽车销量榜

前言 本文是该专栏的第11篇,后面会持续分享python爬虫案例干货,记得关注。 本文以懂车平台的新能源汽车销量榜单为例,获取各车型的销量排行榜单数据。具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。 废话不多说,跟着笔者直接往下看正文详细内容。(附带…

FFMPEG编译安装、简单使用

目录 源码下载编译和安装API简单使用源代码编译运行 工具的简单使用学习论坛 源码下载 地址: https://github.com/FFmpeg/FFmpeg git clone https://github.com/FFmpeg/FFmpeg.git编译和安装 因为使用在板端编译&#xff0c;因此没有使用交叉编译链的部分。将如下内容复制到…

电梯安全远程监控系统的主要作用和意义

电梯是现代城市生活中必不可少的交通工具&#xff0c;为了保证其安全可靠的运行&#xff0c;电梯运行监测系统应运而生。本文将介绍电梯安全远程监控的工作原理、重要性 一、电梯安全远程监控系统的作用   ◆实时监控和故障预警&#xff1a;电梯安全远程监控系统可以实时监测…

加强网站稳定性!学习如何进行高效压力测试!

前言 1、什么是压力测试&#xff1f; 软件压力测试是一种基本的质量保证行为&#xff0c;它是每个重要软件测试工作的一部分。 软件压力测试的基本思路很简单&#xff1a;不是在常规条件下运行手动或自动测试&#xff0c;而是在计算机数量较少或系统资源匮乏的条件下运行测试…

Pandas进阶:文本处理

引言 文本的主要两个类型是string和object。如果不特殊指定类型为string&#xff0c;文本类型一般为object。 文本的操作主要是通过访问器str 来实现的&#xff0c;功能十分强大&#xff0c;但使用前需要注意以下几点。 访问器只能对Series数据结构使用。 除了常规列变量df.c…

从0开始学习JavaScript--JavaScript 集成测试

JavaScript集成测试是确保整个应用程序组件协同工作的关键环节。通过模拟真实环境&#xff0c;集成测试能够发现不同组件之间的潜在问题&#xff0c;确保系统的稳定性和可靠性。本文将深入探讨JavaScript集成测试的核心概念、工具使用以及最佳实践&#xff0c;并通过丰富的示例…

springboot多文件上传

Spring Boot 是一个强大的、灵活的、开发效率高的框架&#xff0c;适合开发各种类型的 Web 应用或微服务&#xff0c;是现代化的企业级开发的首选。 接口编写 可以使用Spring Boot的MultipartResolver来实现上传多文件。 在pom.xml文件中添加以下依赖&#xff1a; <depende…

1.2 Ubauntu 使用

一、完成VMware Tools安装 双击 VMwareTool 打开 Ubuntu 终端快捷键 AltControlT 切换汉语的快捷键是Alt空格 ls 打印出当前所在目录中所有文件和文件夹 cd 桌面 进入桌面文件夹 sudo ./vmware-install.pl 安装tool&#xff0c;输入之前设置的密码。 地址默认&#xff0c;按…

华为OD机试真题-CPU算力分配-2023年OD统一考试(C卷)

题目描述: 现有两组服务器A和B,每组有多个算力不同的CPU,其中A[i]是A组第i个CPU的运算能力,B[i]是B组第i个CPU的运算能力。一组服务器的总算力是各CPU的算力之和。为了让两组服务器的算力相等,允许从每组各选出一个CPU进行一次交换,求两组服务器中,用于交换的CPU的算力,…

UI自动化测试工具有哪些优势?

UI自动化测试工具通过提高测试效率、覆盖率&#xff0c;减少测试时间和成本&#xff0c;以及支持持续集成等方式&#xff0c;为软件开发团队提供了一系列重要的优势&#xff0c;有助于提升软件质量和开发效率。 自动化执行&#xff1a;UI自动化测试工具可以模拟用户与应用程序的…

HarmonyOS脚手架:UI组件之文本和图片

前言 关于HarmonyOS脚手架&#xff0c;本篇是系列的第二篇&#xff0c;主要实现UI组件文本和图片的常见效果查看&#xff0c;本身功能特别的简单&#xff0c;其目的也是很明确&#xff0c;方便大家根据效果查看相关代码实现&#xff0c;可以很方便的进行复制使用&#xff0c;当…

TCP三次握手过程

什么是TCP tcp是一个面向连接的、可靠的、基于字节流的传输层通信协议 面向连接&#xff1a;TCP连接是一对一的&#xff0c;不能实现一对多或多对一&#xff0c;TCP在通信前要首先建立连接&#xff0c;连接成功后才能开始进行通信可靠的&#xff1a;TCP连接要保证通信过程的可靠…