Java集合 HashSet底层详解_hqshset的底层实现-程序员宅基地

技术标签: Java面试  Java集合  HashSet底层原理  

前几篇文章已经介绍了关于List集合的讲解,今天学习Set集合相关的实现类。
Set集合常用的如:HashSet、TreeSet。HashSet是Set集合的典型实现,HashSet按照Hash算法来存储集合中的元素,存在以下特点:

  • 不能保证元素的顺序,元素是无序的
  • HashSet是不同步的,需要外部保持线程之间的同步问题,Collections.synchronizedSet(new XXSet());
  • 集合元素值允许为null

继承关系

 java.util.Collection
    | java.util.AbstractCollection<E> 
        | java.util.AbstractSet<E> 
             | java.util.HashSet<E> 

实现接口

Serializable, Cloneable, Iterable, Collection, Set

基本属性

private transient HashMap<E,Object> map; //map集合,HashSet存放元素的容器
private static final Object PRESENT = new Object(); //map,中键对应的value值

重要方法解析

构造方法

//无参构造方法,完成map的创建
public HashSet() {
    
    map = new HashMap<>();
}
//指定集合转化为HashSet, 完成map的创建
public HashSet(Collection<? extends E> c) {
    
   map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
   addAll(c);
}
//指定初始化大小,和负载因子
public HashSet(int initialCapacity, float loadFactor) {
    
    map = new HashMap<>(initialCapacity, loadFactor);
}
//指定初始化大小
public HashSet(int initialCapacity) {
    
    map = new HashMap<>(initialCapacity);
}
//指定初始化大小和负载因子,dummy 无实际意义
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

通过构造函数可以发现,HashSet底层是采用HashMap实现的。

Add()方法

public boolean add(E e) {
    
    return map.put(e, PRESENT)==null;
}

PRESENT为HashSet类中定义的一个使用static final修饰的常量,其实无实际意义,HashSet的add()方法调用HashMap的put()方法实现,如果键已经存在,map.put()放回的是旧值,添加失败。如果添加成功map.put()方法返回的是null,HashSet.add()方法返回的true,则添加的元素可以作为map中的key。

简单例子:

/**
 * 测试:
 *    1、hashSet不存重复元素
 *    2、存和取是无序的
 * @author Microtao
 *
 */
public class HashSetTest {
    

	public static void main(String[] args) {
    
		Set hs = new HashSet();
		hs.add("a");
		hs.add("b");
		hs.add("1");
		hs.add("2");
		hs.add("e");
		hs.add("e");
		Iterator it = hs.iterator();
		while(it.hasNext()) {
    
			System.out.println(it.next());
		}
	}
}
运行结果:a 1 b 2 e

HashSet存放的是哈希值,Hashset存储元素的顺序并不是按照存入时的顺序(和List显然不同),是按照哈希值来存的,所以取数据也是按照哈希值取的。HashSet不存入重复元素的规则:使用hashcode和equals。 那么HashSet是如何检查重复?其实原理:HashSet会通过元素的hashcode()和equals()方法进行判断,当试图将元素加入到Set集合中,HashSet首先会使用对象的hashcode来判断对象加入的位置。同时也会与其他已经加入的对象的hashcode进行比较,如果没有相等的hashcode,HashSet就认为这个对象之前不存在,如果之前存在同样的hashcode值,就会进一步的比较equals()方法,如果equals()比较返回结果是true,那么认为该对象在集合中的对象是一模一样的,不会将其加入;如果比较返回的是false,那么HashSet认为新加入的对象没有重复,可以正确加入。 如图所示:当两个对象的hashcode不一样时,说明两个对象是一定不相等的,在存储时如左图所示,当两个对象的hashcode相等,但是equals()不相等,在实际中,会在同一个位置,用链式结构来保存多个对象,而HashSet访问集合元素时也是根据元素的hashCode值快速定位,如果HashSet中两个以上的元素具有相同的hashCode值,将会导致性能下降。
在这里插入图片描述hash算法的功能是能保证快速查找被检索的对象,hash算法的价值在于速度,当需要查询集合中某个元素时,hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置,从而快速定位该元素。
简单测试:

package com.microtao.set;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * 测试:
 *   重写hashCode和equals方法,作为判断两者是否相等
 *    
 * @author Microtao
 *
 */

class Per{
    
	private String name;
	private int age;
	private String sex;
	
	public String getName() {
    
		return name;
	}
	public void setName(String name) {
    
		this.name = name;
	}
	public int getAge() {
    
		return age;
	}
	public void setAge(int age) {
    
		this.age = age;
	}
	public String getSex() {
    
		return sex;
	}
	public void setSex(String sex) {
    
		this.sex = sex;
	}
	
	public Per(String name, int age, String sex) {
    
		super();
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	@Override
	public String toString() {
    
		return "Per [name=" + name + ", age=" + age + ", sex=" + sex + "]";
	}
	@Override
	public int hashCode() {
    
		return this.age * 31;
	}
	@Override
	public boolean equals(Object obj) {
    
		if(obj != null) {
    
			if(obj instanceof Per) {
    
				Per p = (Per) obj;
				return this.name.equals(p.name) && this.age == p.age;
			}
		}
		return false;
	}
	
}
public class HashSetTest {
    

	public static void main(String[] args) {
    
		Set hs = new HashSet();
		hs.add(new Per("zhangsan",12,"男"));
		hs.add(new Per("lisi",12,"女"));
		hs.add(new Per("wangwu",12,"男"));
		hs.add(new Per("zhaoliu",12,"男"));
		hs.add(new Per("zhaoliu",12,"女"));
		Iterator it = hs.iterator();
		while(it.hasNext()) {
    
			System.out.println(it.next());
		}
	}
}
运行结果:
		Per [name=zhangsan, age=12, sex=]
		Per [name=lisi, age=12, sex=]
		Per [name=wangwu, age=12, sex=]
		Per [name=zhaoliu, age=12, sex=]

上面的运行可以看出,name = zhaoliu ,age = 12有两个,但是在sex字段的值是不一样的,而在前面判断两个对象是否相等时,没有将sex字段考虑进去,所以会认为两者时一样的,所以在判断对象是否相等时,是通过计算两者的hashCode值,和equals方法进行的,这是作为Set集合判断不存重复元素的根本原因。

总结;
1、使用HashSet集合时, 首先应该知道它是无序的,其次不存在重复元素。
2、如何判断是否是重复的元素也是应该很清楚的
3、如果计算两者的hashCode值一样,但是equals不一样,也是不一样的对象,在存储时,会采用链式结构进行存储。

【参考资料】 https://blog.csdn.net/qq_33642117/article/details/52040345

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yt_19940616/article/details/90206441

智能推荐

IOS面试题object-c 81-90-程序员宅基地

文章浏览阅读361次。包括:NSURLRequest、NSURLCache、NSURLSession、NSURLSessionConfiguration、NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask。,您可以使用 iPhone OS 上的独特的图形接口控件,按钮,以及全屏视图的功能,您还可以使用加速仪和多点触摸手势来控制您的应用。都是等效的setObject:forKey:.在其他类中,setValue:forKey:更改成员变量.

公司综合管理系统详细设计与具体代码实现-程序员宅基地

文章浏览阅读310次,点赞3次,收藏6次。1. 背景介绍1.1 公司管理系统的重要性在当今快节奏的商业环境中,高效的公司管理系统对于确保企业的顺利运营至关重要。随着公司规模的不断扩大和业务复杂度的增加,传统的手工管理方式已经无法满足现代企业的需求。因此,开发一个综合的公司管理系统来集中管理公司的各个方面,如人力资源、财务、项目、客户

基于Hog+SVM实现小狮子的识别_hog+svm小狮子-程序员宅基地

文章浏览阅读275次。1从视频中获取图片安装opencvpip3 install opencv-python# 视频分解成图片# 1 load加载视频 2 读取info 3 解码 单帧视频parse 4 展示 imshowimport cv2# 获取一个视频打开capcap = cv2.VideoCapture('1.mp4')# 判断是否打开isOpened = cap.isOpenedprint(isOpened)#帧率fps = cap.get(cv2.CAP_PROP_FPS)#宽度wid_hog+svm小狮子

android的/system/lib/libhwui.so崩溃分析和解决办法-程序员宅基地

文章浏览阅读1.2w次,点赞2次,收藏2次。直接上崩溃日志了:#00 pc 00039518 (null)#01 pc 00022ef9 /system/lib/libhwui.so [armeabi-v7a]#02 pc 00015d7d /system/lib/libhwui.so [armeabi-v7a]#03 pc 0..._libhwui.so

spring ioc原理,IoC与DI-网摘-程序员宅基地

文章浏览阅读42次。首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号……...

建模算法(八)——插值-程序员宅基地

文章浏览阅读111次。插值:求过已知有限个数据点的近似函数 拟合:已知有限个数据点,求近似函数,不要求过已知数据点,只要求在某种意义下在这些点的误差最小 (一)插值方法 一、拉格朗日多项式插值 1、插值多项式 就是做出一个多项式函数,经过给出的n个节点,并尽可能的接近原函数,将点带入多项式函数得到一个线性方程组 当系数矩阵满秩时,有唯一解。而,系数矩阵的行列式为 这是..._取 n+1个等距节点做 lagrange 插值近似该函数,画出 latex: n = 4, 7, 10n = 4 , 7

随便推点

UE5 GAS开发P41-43 永久效果,去除永久效果,伤害区域,EnumClass,开始重叠与结束重叠事件

这一部分学习了怎么创建一个伤害性的地形(火焰地形,毒沼泽等都可以用这个方式创建)AuraEffectActor.h// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameplayEffect.h" #include "AbilitySystem/AuraAbilitySystemComponentBase

Anaconda安装(过程详细)_anaconda安装教程-程序员宅基地

文章浏览阅读10w+次,点赞392次,收藏1.4k次。本文将详细介绍Anaconda的安装过程。_anaconda安装教程

运用Aop思想存储日志_aop操作日志思想-程序员宅基地

文章浏览阅读226次。一、Aop思想:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。二、AOP中的相关概念Aspect(切面): Aspect 声明类似于 Java 中的_aop操作日志思想

linux编译mysql 库_【LINUX】Linux mysql数据库搭建(编译安装)-程序员宅基地

文章浏览阅读84次。版本为linux6.4,首先下载编译安装包至本地。设共享软件包地址192.168.80.10setenforce 0service iptables stop1.共享软件包mount.cifs //192.168.80.10/r /media/ 匿名访问共享文件夹cd /media/ls 查看是否挂载成功了tar xzvf mysq..._linux qt 6.4 编译mysql

用apiCloud开发应用-程序员宅基地

文章浏览阅读62次。使用apiCloud开发应用就是用html5写页面,css实现样式,js写功能。一套代码在android和ios上都能运行。节省开发周期和人员开销。代码可以放到云服务器,可以云端打包,云端更新。apicloud提供了一般开发用到的接口,封装了很多模块,在http://www.apicloud.com/dev可以看到其开发文档。缺点:与原生开发相比较对目前对底层的操作还没有那个么的得心应..._什么应用是apicloud开发的

G-sensor概述及常用芯片整理_int1_src lis2dh12 csdn-程序员宅基地

文章浏览阅读5.7k次,点赞6次,收藏34次。本文对G-sensor进行整理,先介绍G-sensor的一些基本概念,再具体讲解BOSCH、ST、ADI三家的G-sensor,其中BOSCH的G-sensor重点讲BMA222E,ST的G-sensor重点讲LIS2DH12,ADI的G-sensor具体讲ADXL362。一、G-sensor概述什么是MEMSMEME(Micro-Electro-Mechanical System),..._int1_src lis2dh12 csdn