当前位置: 首页 > 科技观察

考察基础部分,说说Java集合中HashSet的原理和常用方法

时间:2023-03-19 20:49:15 科技观察

分类HashSet概述一个实现类,Set是一个接口,它的实现类不仅是HashSet,还有TreeSet,并且继承了Collection,HashSet集合是非常常用的,也是程序员面试时经常被问到的一个知识点。下面是结构图publicclassHashSetextendsAbstractSetimplementsSet,Cloneable,java.io.Serializable{}2.HashSet构造HashSet有几种重载的构造方法,我们来看看privatetransientHashMapmap;//默认构造函数publicHashSet(){map=newHashMap<>();}//将传入集合添加到HashSet构造函数中publicHashSet(Collectionc){map=newHashMap<>(Math.max((int)(c.size()/.75f)+1,16));addAll(c);}//指定初始容量和加载因子的构造函数publicHashSet(ininitialCapacity,floatloadFactor){map=newHashMap<>(initialCapacity,loadFactor);}//只指定初始容量的构造函数(加载因子默认0.75)publicHashSet(ininitialCapacity){map=newHashMap<>(initialCapacity);}通过上面的源码,我们发现HashSet只是一个bagcompany,它把job外部化了,接收到job后,直接丢给HashMap处理。因为底层是通过HashMap实现的,这里简单提一下:HashMap的数据存储是通过数组+链表/红黑树实现的,大概的存储过程就是通过hash计算数组中的存储位置功能。现在判断key是否相同,相同则覆盖,不相同则放入元素对应的链表。如果链表长度大于8,则转为红黑树。如果容量不够,就需要扩容(注:这只是一般流程)。过往问题:100道面试题总结3.add方法HashSet的add方法是通过HashMap的put方法实现的,但是HashMap是key-value键值对,而HashSet是集合,那么它是如何存储的呢?来看看源码privatestaticfinalObjectPRESENT=newObject();publicbooleanadd(Ee){returnmap.put(e,PRESENT)==null;}查看源码知道HashSet添加的元素存放在HashMap的key位置,value取默认常量PRESENT为一个空对象。关于map的put方法可以看https://www.cnblogs.com/LiaHon/p/11149644.html4.remove方法HashSet的remove方法是通过HashMap的remove方法实现的//HashSet的remove方法publicbooleanremove(Objecto){returnmap.remove(o)==PRESENT;}//map的remove方法publicVremove(Objectkey){Nodee;//通过hash(key)在数组中找到元素位置,调用removeNode方法删除return(e=removeNode(hash(key),key,null,false,true))==null?null:e.value;}/****/finalNoderemoveNode(inthash,Objectkey,Objectvalue,booleanmatchValue,booleanmovable){Node[]tab;Nodep;intn,index;//第一步,需要找到对应的Nodekey的具体位置,首先通过(n-1)&hash)&hash])!=null){Nodenode=null,e;kk;Vv;//1.1如果这个节点刚好有相同的key值,祝你好运,找到if(p.hash==hash&&((k=p.key)==key||(key!=null&&key.equals(k))))node=p;/***1.2运气不好,虽然在数组中找到的Nodehash相同,但是key值不一样,显然不对,需要遍历继续*寻找;*/elseif((e=p.next)!=null){//1.2.1如果是TreeNode类型,说明HashMap当前是通过数组+红黑树存储的,遍历红黑tree寻找对应的节点if(pinstanceofTreeNode)node=((TreeNode)p).getTreeNode(hash,key);else{//1.2.2如果是链表,则遍历链表寻找对应的nodedo{if(e.hash==hash&&((k=e.key)==key||(key!=null&&key.equals(k)))){node=e;break;}p=e;}while((ee=e.next)!=null);}}//我们通过前面的step1找到了对应的Node,现在需要删除它if(node!=null&&(!matchValue||(v=node.value)==value||(value!=null&&value.equals(v)))){/***如果是TreeNode类型,de删除方法是通过删除红黑树节点来实现的。详见【TreeMap原理实现*及常用方法】*/if(nodeinstanceofTreeNode)((TreeNode)node).removeTreeNode(this,tab,movable);/***如果是a链表,当找到的节点是数组hash位置的第一个元素,则删除该元素后,数组*第一个位置的引用指向链表的下一个*/elseif(node==p)tab[index]=node.next;/***如果找到的节点本来就是链表上的节点,也简单,将删除该节点的前一个节点的next指向*下一个要删除的节点,只需要隔离要删除的节点*/elsep.next=node.next;++modCount;--size;//删除后可能会有存储结构调整,请参考【如何保证顺序】中的remove方法afterNodeRemoval(node);returnnode;}}returnnullLinkedHashMap]}removeTreeNode方法具体实现可以参考【第144期】考试基础部分,能说说TreeMap的实现原理和常用方法吗?afterNodeRemoval方法的具体实现可以参考https://www.cnblogs.com/LiaHon/p/11180869.html5.将HashSet作为一个集合进行遍历,遍历的方法有很多种,比如普通的for循环,增强的for循环,迭代器,我们通过迭代器遍历看一下");setString.add("星期三");setString.add("星期四");setString.add("星期五");Iteratorit=setString.iterator();while(it.hasNext()){System.out.println(it.next());}}打印出来的结果是什么?不出所料,周二、周三、周四、周五、周一,HashSet是通过HashMap实现的,HashMap是通过hash(key)来确定存储位置的,没有存储顺序,所以HashSet遍历的元素没有按照Sequence插入6.Total按照我之前的计划,每个主要的内容都要单独写,比如集合ArrayList、LinkedList、HashMap、TreeMap等。然而,当我在写这篇关于HashSet的文章时,发现经过前面对HashMap的讲解,其实很简单。HashSet是一家皮包公司,在HashMap外加了一个壳。那么LinkedHashSet是不是只是在LinkedHashMap外面加了一个壳?那么TreeSet是不是在TreeMap外面加了一个壳呢?我们来验证一下。我们先看一下LinkedHashSet的初始结构图。前面提到LinkedHashSet是HashSet的子类。让我们看一下源代码。initialCapacity,floatloadFactor){super(initialCapacity,loadFactor,true);}publicLinkedHashSet(ininitialCapacity){super(initialCapacity,.75f,true);}publicLinkedHashSet(){super(16,.75f,true);}publicLinkedHashSet(Collection<?extendsE>c){super(Math.max(2*c.size(),11),.75f,true);addAll(c);}publicSpliteratorspliterator(){returnSpliterators.spliterator(this,Spliterator.DISTINCT|Spliterator.ORDERED);}}以上就是LinkedHashSet的全部代码。是不是觉得智商被否定了?这基本上没什么。构造函数都调用父类。下面是父类HashSet,这个的构造方法是HashSet(ininitialCapacity,floatloadFactor,booleandummy){map=newLinkedHashMap<>(initialCapacity,loadFactor);},可以看到,和我们的猜测是一样的,没必要深究。有兴趣的可以看看LinkedHashMap是如何保证顺序的。看一下TreeSetpublicclassTreeSetextendsAbstractSetimplementsNavigableSet,Cloneable,java.io.Serializable{publicTreeSet(){this(newTreeMap());}publicTreeSet(Comparator比较器){this(newTreeMap<>(比较器));}publicTreeSet(Collectionc){this();addAll(c);}publicTreeSet(SortedSets){this(s.comparator());addAll(s);}}确实和我们猜测的一样,TreeSet也是完全依赖TreeMap来实现的,有兴趣的可以看看TreeMap的原理实现和常用方法7.总结一下内容三章,一章结束,虽然Set的实现有点草率,毕竟他的祖宗是Collection而不是Map,在MapSet的实现类上就成了一层衣服,然后埋伏在Collection中进行somepurpose,哈哈,开个玩笑,本文主要介绍了HashSet的原理和主要方法,同时也简单介绍了LinkedHashSet和TreeSet,如有不妥之处,敬请批评指正,望配合进步,谢谢!