当前位置: 首页 > Web前端 > JavaScript

记得上线的一个set去重导致的问题

时间:2023-03-26 22:09:36 JavaScript

前言工作中查看界面慢的时候,发现了一个耗时很久的函数,最后通过List去重定位。由于缺乏早期测试环境和线上的数据,这个接口的性能问题一直没有引起太多关注,后来频繁超时才引起关注。.HashSet源码查看类注解,我们可以得到的信息:底层实现是基于HashMap的,所以迭代时不能保证按照插入顺序或者其他顺序迭代;add、remove、contanins、size等方法的耗时性能是不会随着数据量的增加而增加的。这主要和HashMap底层的数组数据结构有关。不管数据量有多大,不管哈希冲突,时间复杂度都是O(1);线程不安全如果需要安全,请自己加锁,或者使用Collections.synchronizedSet;在迭代过程中,如果改变了数据结构,会很快失败,抛出ConcurrentModificationException。刚才从类注解上看到HashSet的实现是基于HashMap的。在Java中,基于基类实现创新有两种方式:继承基类,重写基类的方法,例如继承HashMap,重写其add方法;结合基础类,通过调用基础类的方法,复用基础类的能力。HashSet使用了HashMap的组合,它的优点有:继承就是父类和子类是同一个东西,而Set和Map本来就是想表达两个东西,所以继承不合适,Java语法受限,子类只能继承一个父类,以后很难扩展。组合更灵活。现有的基础类可以任意组合,可以基于基础类的方法进行扩展、编排等,方法名可以任意命名,不需要和基础类的方法名保持一致.组合就是把HashMap当成自己的一个局部变量。下面是HashSet的组合实现://CombineHashMap,key是Hashset的key,value是下面的PRESENTprivatetransientHashMapmap;//HashMap中的valueprivatestaticfinalObjectPRESENT=newObject();从这两行代码我们可以看出两点:我们在使用HashSet的时候,比如add方法只有一个入参,但是组合Map的add方法有Key和value是两个入参。对应的,Map的key就是我们add的入参,value就是第二行代码中的PRESENT。这里的设计很巧妙,用一个默认值PRESENT来代替Map的Value;我们再来看看add方法:publicbooleanadd(Ee){//直接使用HashMap的put方法做一些简单的逻辑判断returnmap.put(e,PRESENT)==null;}让我们进入底层源代码java.util.HashMap#put:publicVput(Kkey,Vvalue){returnputVal(hash(key),key,value,false,true);}