游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

一次List对象去重失败,引发对Java8中distinct

  作者:puppylpg
  blog。csdn。netpuppylpgarticledetails78556730list的转map的另一种猜想
  Java8使用lambda表达式进行函数式编程可以对集合进行非常方便的操作。一个比较常见的操作是将list转换成map,一般使用Collectors的toMap()方法进行转换。一个比较常见的问题是当list中含有相同元素的时候,如果不指定取哪一个,则会抛出异常。因此,这个是必须的。
  当然,使用toMap()的另一个重载方法,可以直接指定。这里,我们想讨论的是另一种方法:在进行转map的操作之前,能不能使用distinct()先把list的重复元素过滤掉,然后转map的时候就不用考虑重复元素的问题了。使用distinct()给list去重直接使用distinct(),失败packageexample。mystream;importlombok。AllArgsConstructor;importlombok。Getter;importlombok。NoArgsConstructor;importlombok。ToString;importjava。util。Arrays;importjava。util。List;importjava。util。Map;importjava。util。stream。Collectors;publicclassListToMap{AllArgsConstructorNoArgsConstructorToStringprivatestaticclassVideoInfo{GetterStringid;intwidth;intheight;}publicstaticvoidmain(String〔〕args){ListVideoInfolistArrays。asList(newVideoInfo(123,1,2),newVideoInfo(456,4,5),newVideoInfo(123,1,2));preferred:handleduplicateddatawhentoMap()MapString,VideoInfoid2VideoInfolist。stream()。collect(Collectors。toMap(VideoInfo::getId,xx,(oldValue,newValue)newValue));System。out。println(NoDuplicated1:);id2VideoInfo。forEach((x,y)System。out。println(x,y));handleduplicateddatausingdistinct(),beforetoMap()MapString,VideoInfoid2VideoInfo2list。stream()。distinct()。collect(Collectors。toMap(VideoInfo::getId,xx));System。out。println(NoDuplicated2:);id2VideoInfo2。forEach((x,y)System。out。println(x,y));}}
  list里总共有三个元素,其中有两个我们认为是重复的。第一种转换是使用toMap()直接指定了对重复key的处理情况,因此可以正常转换成map。而第二种转换是想先对list进行去重,然后再转换成map,结果还是失败了,抛出了IllegalStateException,所以distinct()应该是失败了。NoDuplicated1:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)Exceptioninthreadmainjava。lang。IllegalStateException:DuplicatekeyListToMap。VideoInfo(id123,width1,height2)atjava。util。stream。Collectors。lambdathrowingMerger0(Collectors。java:133)atjava。util。HashMap。merge(HashMap。java:1253)atjava。util。stream。Collectors。lambdatoMap58(Collectors。java:1320)atjava。util。stream。ReduceOps3ReducingSink。accept(ReduceOps。java:169)atjava。util。stream。DistinctOps12。accept(DistinctOps。java:175)atjava。util。SpliteratorsArraySpliterator。forEachRemaining(Spliterators。java:948)atjava。util。stream。AbstractPipeline。copyInto(AbstractPipeline。java:481)atjava。util。stream。AbstractPipeline。wrapAndCopyInto(AbstractPipeline。java:471)atjava。util。stream。ReduceOpsReduceOp。evaluateSequential(ReduceOps。java:708)atjava。util。stream。AbstractPipeline。evaluate(AbstractPipeline。java:234)atjava。util。stream。ReferencePipeline。collect(ReferencePipeline。java:499)atexample。mystream。ListToMap。main(ListToMap。java:79)原因:distinct()依赖于equals()
  查看distinct()的API,可以看到如下介绍:
  Returnsastreamconsistingofthedistinctelements(accordingto{linkObjectequals(Object)})ofthisstream。
  显然,distinct()对对象进行去重时,是根据对象的equals()方法去处理的。如果我们的VideoInfo类不overrride超类Object的equals()方法,就会使用Object的。
  但是Object的equals()方法只有在两个对象完全相同时才返回true。而我们想要的效果是只要VideoInfo的idwidthheight均相同,就认为两个videoInfo对象是同一个。所以我们比如重写属于videoInfo的equals()方法。重写equals()的注意事项
  我们设计VideoInfo的equals()如下:Overridepublicbooleanequals(Objectobj){if(!(objinstanceofVideoInfo)){returnfalse;}VideoInfovi(VideoInfo)obj;returnthis。id。equals(vi。id)this。widthvi。widththis。heightvi。height;}
  这样一来,只要两个videoInfo对象的三个属性都相同,这两个对象就相同了。欢天喜地的去运行程序,依旧失败!why?
  《EffectiveJava》是本好书,连Java之父JamesGosling都说,这是一本连他都需要的Java教程。在这本书中,作者指出,如果重写了一个类的equals()方法,那么就必须一起重写它的hashCode()方法!必须!没有商量的余地!
  必须使得重写后的equals()满足如下条件:根据equals()进行比较,相等的两个对象,hashCode()的值也必须相同;根据equals()进行比较,不相等的两个对象,hashCode()的值可以相同,也可以不同;
  因为这是Java的规定,违背这些规定将导致Java程序运行不再正常。
  具体更多的细节,建议大家读读原书,必定获益匪浅。强烈推荐!
  最终,我按照神书的指导设计VideoInfo的hashCode()方法如下:OverridepublicinthashCode(){intn31;nn31this。id。hashCode();nn31this。height;nn31this。width;returnn;}
  终于,distinct()成功过滤了list中的重复元素,此时使用两种toMap()将list转换成map都是没问题的:NoDuplicated1:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)NoDuplicated2:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)引申
  既然说distinct()是调用equals()进行比较的,那按照我的理解,list的3个元素至少需要比较3次吧。那是不是就调用了3次equals()呢?
  在equals()中加入一句打印,这样就可以知道了。加后的equals()如下:Overridepublicbooleanequals(Objectobj){if(!(objinstanceofVideoInfo)){returnfalse;}VideoInfovi(VideoInfo)obj;System。out。println(Invokeequals()this。toString()vs。vi。toString());returnthis。id。equals(vi。id)this。widthvi。widththis。heightvi。height;}
  结果:NoDuplicated1:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)Invokeequals()ListToMap。VideoInfo(id123,width1,height2)vs。ListToMap。VideoInfo(id123,width1,height2)NoDuplicated2:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)
  结果发现才调用了一次equals()。为什么不是3次呢?仔细想想,根据hashCode()进行比较,hashCode()相同的情况就一次,就是list的第一个元素和第三个元素(都是VideoInfo(id123,width1,height2))会出现hashCode()相同的情况。
  所以我们是不是可以这么猜想:只有当hashCode()返回的hashCode相同的时候,才会调用equals()进行更进一步的判断。如果连hashCode()返回的hashCode都不同,那么可以认为这两个对象一定就是不同的了!
  验证猜想:
  更改hashCode()如下:OverridepublicinthashCode(){return1;}
  这样一来,所有的对象的hashCode()返回值都是相同的。当然,这样搞是符合Java规范的,因为Java只规定equals()相同的对象的hashCode必须相同,但是不同的对象的hashCode未必会不同。
  结果:NoDuplicated1:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)Invokeequals()ListToMap。VideoInfo(id456,width4,height5)vs。ListToMap。VideoInfo(id123,width1,height2)Invokeequals()ListToMap。VideoInfo(id456,width4,height5)vs。ListToMap。VideoInfo(id123,width1,height2)Invokeequals()ListToMap。VideoInfo(id123,width1,height2)vs。ListToMap。VideoInfo(id123,width1,height2)NoDuplicated2:123,ListToMap。VideoInfo(id123,width1,height2)456,ListToMap。VideoInfo(id456,width4,height5)
  果然,equals()调用了三次!看来的确只有hashCode相同的时候才会调用equal()进一步判断两个对象究竟是否相同;如果hashCode不相同,两个对象显然不相同。猜想是正确的。结论list转map推荐使用toMap(),并且无论是否会出现重复的问题,都要指定重复后的取舍规则,不费功夫但受益无穷;对一个自定义的class使用distinct(),切记覆写equals()方法;覆写equals(),一定要覆写hashCode();虽然设计出一个hashCode()可以简单地让其return1,这样并不会违反Java规定,但是这样做会导致很多恶果。比如将这样的对象存入hashMap的时候,所有的对象的hashCode都相同,最终所有对象都存储在hashMap的同一个桶中,直接将hashMap恶化成了一个链表。从而O(1)的复杂度被整成了O(n)的,性能自然大大下降。好书是程序员进步的阶梯。高尔基。比如《EffecctiveJava》。
  最终参考程序:packageexample。mystream;importlombok。AllArgsConstructor;importlombok。Getter;importlombok。NoArgsConstructor;importlombok。ToString;importjava。util。Arrays;importjava。util。List;importjava。util。Map;importjava。util。stream。Collectors;publicclassListToMap{AllArgsConstructorNoArgsConstructorToStringprivatestaticclassVideoInfo{GetterStringid;intwidth;intheight;publicstaticvoidmain(String〔〕args){System。out。println(newVideoInfo(123,1,2)。equals(newVideoInfo(123,1,2)));}Overridepublicbooleanequals(Objectobj){if(!(objinstanceofVideoInfo)){returnfalse;}VideoInfovi(VideoInfo)obj;returnthis。id。equals(vi。id)this。widthvi。widththis。heightvi。height;}Ifequals()isoverride,hashCode()mustbeoverride,too。1。ifaequalsb,theymusthavethesamehashCode;2。ifadoesntequalsb,theymayhavethesamehashCode;3。hashCodewritteninthiswaycanbeaffectedbysequenceofthefields;3。25131。So31willbefasterwhendothemultiplication,becauseitcanbereplacedbybitshifting:31i(i5)i。returnOverridepublicinthashCode(){intn31;nn31this。id。hashCode();nn31this。height;nn31this。width;returnn;}}publicstaticvoidmain(String〔〕args){ListVideoInfolistArrays。asList(newVideoInfo(123,1,2),newVideoInfo(456,4,5),newVideoInfo(123,1,2));preferred:handleduplicateddatawhentoMap()MapString,VideoInfoid2VideoInfolist。stream()。collect(Collectors。toMap(VideoInfo::getId,xx,(oldValue,newValue)newValue));System。out。println(NoDuplicated1:);id2VideoInfo。forEach((x,y)System。out。println(x,y));handleduplicateddatausingdistinct(),beforetoMap()Notethatdistinct()reliesonequals()intheobjectifyouoverrideequals(),hashCode()mustbeoverridetogetherMapString,VideoInfoid2VideoInfo2list。stream()。distinct()。collect(Collectors。toMap(VideoInfo::getId,xx));System。out。println(NoDuplicated2:);id2VideoInfo2。forEach((x,y)System。out。println(x,y));}}再拓展假设类是别人的,不能修改
  以上,VideoInfo使我们自己写的类,我们可以往里添加equals()和hashCode()方法。如果VideoInfo是我们引用的依赖中的一个类,我们无权对其进行修改,那么是不是就没办法使用distinct()按照某些元素是否相同,对对象进行自定义的过滤了呢?使用wrapper
  在stackoverflow的一个回答上,我们可以找到一个可行的方法:使用wrapper。
  假设在一个依赖中(我们无权修改该类),VideoInfo定义如下:AllArgsConstructorNoArgsConstructorToStringpublicclassVideoInfo{GetterStringid;intwidth;intheight;}
  使用刚刚的wrapper思路,写程序如下(当然,为了程序的可运行性,还是把VideoInfo放进来了,假设它就是不能修改的,不能为其添加任何方法):packageexample。mystream;importlombok。AllArgsConstructor;importlombok。Getter;importlombok。NoArgsConstructor;importlombok。ToString;importjava。util。Arrays;importjava。util。List;importjava。util。Map;importjava。util。stream。Collectors;publicclassDistinctByWrapper{privatestaticclassVideoInfoWrapper{privatefinalVideoInfovideoInfo;publicVideoInfoWrapper(VideoInfovideoInfo){this。videoInfovideoInfo;}publicVideoInfounwrap(){returnvideoInfo;}Overridepublicbooleanequals(Objectobj){if(!(objinstanceofVideoInfo)){returnfalse;}VideoInfovi(VideoInfo)obj;returnvideoInfo。id。equals(vi。id)videoInfo。widthvi。widthvideoInfo。heightvi。height;}OverridepublicinthashCode(){intn31;nn31videoInfo。id。hashCode();nn31videoInfo。height;nn31videoInfo。width;returnn;}}publicstaticvoidmain(String〔〕args){ListVideoInfolistArrays。asList(newVideoInfo(123,1,2),newVideoInfo(456,4,5),newVideoInfo(123,1,2));VideoInfomap()VideoInfoWrapperdistinct():VideoInfoWrappermap()VideoInfoMapString,VideoInfoid2VideoInfolist。stream()。map(VideoInfoWrapper::new)。distinct()。map(VideoInfoWrapper::unwrap)。collect(Collectors。toMap(VideoInfo::getId,xx,(oldValue,newValue)newValue));id2VideoInfo。forEach((x,y)System。out。println(x,y));}}AssumethatVideoInfoisaclassthatwecantmodifyAllArgsConstructorNoArgsConstructorToStringclassVideoInfo{GetterStringid;intwidth;intheight;}
  整个wrapper的思路无非就是构造另一个类VideoInfoWrapper,把hashCode()和equals()添加到wrapper中,这样便可以按照自定义规则对wrapper对象进行自定义的过滤。
  我们没法自定义过滤VideoInfo,但是我们可以自定义过滤VideoInfoWrapper啊!
  之后要做的,就是将VideoInfo全部转化为VideoInfoWrapper,然后过滤掉某些VideoInfoWrapper,再将剩下的VideoInfoWrapper转回VideoInfo,以此达到过滤VideoInfo的目的。很巧妙!使用filter()自定义函数取代distinct()
  另一种更精妙的实现方式是自定义一个函数:privatestaticTPredicateTdistinctByKey(Functionlt;?superT,ObjectkeyExtractor){MapObject,BooleanmapnewConcurrentHashMap();returntmap。putIfAbsent(keyExtractor。apply(t),Boolean。TRUE)null;}
  (输入元素的类型是T及其父类,keyExtracctor是映射函数,返回Object,整个传入的函数的功能应该是提取key的。distinctByKey函数返回的是Predicate函数,类型为T。)
  这个函数传入一个函数(lambda),对传入的对象提取key,然后尝试将key放入concurrentHashMap,如果能放进去,说明此key之前没出现过,函数返回false;如果不能放进去,说明这个key和之前的某个key重复了,函数返回true。
  这个函数最终作为filter()函数的入口。根据JavaAPI可知filter(func)过滤的规则为:如果func为true,则过滤,否则不过滤。因此,通过filter()自定义的函数,凡是重复的key都返回true,并被filter()过滤掉,最终留下的都是不重复的。
  最终实现的程序如下packageexample。mystream;importlombok。AllArgsConstructor;importlombok。Getter;importlombok。NoArgsConstructor;importlombok。ToString;importjava。util。Arrays;importjava。util。List;importjava。util。Map;importjava。util。concurrent。ConcurrentHashMap;importjava。util。function。Function;importjava。util。function。Predicate;importjava。util。stream。Collectors;publicclassDistinctByFilterAndLambda{publicstaticvoidmain(String〔〕args){ListVideoInfolistArrays。asList(newVideoInfo(123,1,2),newVideoInfo(456,4,5),newVideoInfo(123,1,2));GetdistinctonlyMapString,VideoInfoid2VideoInfolist。stream()。filter(distinctByKey(vivi。getId()))。collect(Collectors。toMap(VideoInfo::getId,xx,(oldValue,newValue)newValue));id2VideoInfo。forEach((x,y)System。out。println(x,y));}IfakeycouldnotbeputintoConcurrentHashMap,thatmeansthekeyisduplicatedparamkeyExtractoramappingfunctiontoproducekeysparamTthetypeoftheinputelementsreturntrueifkeyisduplicated;elsereturnfalseprivatestaticTPredicateTdistinctByKey(Functionlt;?superT,ObjectkeyExtractor){MapObject,BooleanmapnewConcurrentHashMap();returntmap。putIfAbsent(keyExtractor。apply(t),Boolean。TRUE)null;}}AssumethatVideoInfoisaclassthatwecantmodifyAllArgsConstructorNoArgsConstructorToStringclassVideoInfo{GetterStringid;intwidth;intheight;}

提升生产力的新工具,这次我选择了华为Matepad11扬州疫情期间,自居家办公已经1月有余,目前已经实现了零增长,无论如何,这个消息都是值得庆幸的。作为一个数码爱好者,自认为对使用的电子设备还是比较挑剔的,虽然钱袋有限,但是对于自……苹果的难题谁会是下一个库克?8月25日,是库克上任十周年纪念日。分析师本伍德说:想当年,库克刚接任,大家都在说他不行,没有乔布斯那种魅力。其实深挖下去,库克任期内的成绩令人惊叹。以苹果今时今日的地位和成绩……戴上助听器后听自己声音沙哑什么原因?你好,戴上助听器后听自己声音沙哑什么原因?戴上助听器会有堵耳效应,可以更换耳模、耳塞来降低不适感。如果还是不舒服,应该进行增益的调节,初次佩戴都会感觉自己说话有改变,只要……特斯拉又遇到麻烦了市值瞬间蒸发2200亿蔚来因为自动驾驶致死的交通事故还没有查明,特斯拉的自动驾驶就又出大事了!8月16日,美国国家公路交通安全管理局(NHTSA)宣布,美国政府对特斯拉自动驾驶系统启动正式调查……学生最需要的一款数码产品,性价比蓝牙耳机时间匆匆来到8月中旬,很多准大一新生估计还在为大学生活需要带的物品烦恼不已,但师姐必须提醒各位同学一定要带上一副蓝牙耳机哦,因为大学面对来自五湖四海的同学们难免需要一些自我空间……华为MateX2,折叠屏手机新标杆?曾有圈内人士说,折叠屏形态是智能手机的下一个未来,如今,未来已来,且还超出了许多人的预期。作为一枚数码控,对于新潮的产品,都会把持不住想要尝鲜,由于一些原因,经过几个月漫……原材料上涨推高成本比亚迪等新能源车企掀起涨价潮本报记者矫月见习记者李昱丞新能源汽车的成本压力正向需求端传导。3月15日夜间,比亚迪在官方微博发布通知,自3月16日零时起对王朝网和海洋网相关新能源车型的官方指导价进行调……走进长三角走入G60,境外媒体到访思必驰总部为深入贯彻落实支持长江三角洲区域一体化发展并上升为国家战略,全面反映长三角区域科创驱动高质量一体化发展情况,扩大长三角G60科创走廊的国际的影响力。在中国国际新闻交流中心的大力……思必驰智能硬件新品发布,宾狗智造精彩生活2021年5月18日,会声慧色2021思必驰智能硬件新品发布会在京顺利召开,吸引了包括腾讯、新浪、搜狐、今日头条、新华网、极果网等数十家主流媒体聚焦现场。一直以来,作为国内领先……对话爱情,雷柏ralemoPre5蓝牙机械键盘告白季献礼喜欢一个人常常内心风起云涌,表面仍波澜不惊。那些情窦初开的瞬间,在心动的时候都能将人变成青涩的模样。爱情它来的时候,捎着明媚日光和剔透雨露,在心间洒落一片灿烂温煦。温热气息裹挟……美国CPC认证(亚马逊必备)儿童产品证书ampampamp儿近期,美国亚马逊平台因产品没有提供CPC证书而下架产品的案例频繁发生。该平台下架的产品类别较多,如儿童玩具、儿童首饰品、儿童学习用品等。那么CPC认证具体有什么要求,如何为买家……每4分钟售出3台新车!第4代帝豪预售订单破万,欢迎到店体验日前,中国品牌轿车领军者吉利汽车第4代帝豪开启预售,截至8月8日订单已超过1万,平均每4分钟就售出3台新车,充分展现出第4代帝豪强劲的市场竞争力。为感恩回馈用户,吉利汽车追加了……
艾创米指纹锁TK01天空一号手机远程猫眼说明书首先,在手机应用商城免费下载手机APP《超级看看》软件,如图1一、登录:打开软件,进入账号登录界面,如图2,点击左下角的注册;新用户请使用手机号码注册,按提示输入自己的用……丁香花开,香满边城最近几天,黑河的丁香花开了。小区、路边,特别是黑龙江畔的带状公园里一丛丛的,粉红、紫红,间或也有粉白的花朵一串串的在初夏的晨风中摇曳着。近几年,在黑龙江公园里修了好几公里……续航305km三门两座布局,五菱全新电动车来了,或售67万?在我眼中,五菱是一个能精准把握市场机遇的车企,以往的五菱宏光、五菱之光且不谈。就拿近两年五菱发布的新车来看,大四座家用车五菱凯捷,上市不久便坐上了同级销量前三的宝座;电动车时代……厨房墙砖怎么选?这篇攻略告诉你厨房是一个实用性很强的地方,因此不会侧重于装饰上,想要拥有颜值高的厨房,可以从厨房墙砖下手,因此厨房墙砖的种类及如何挑选就非常重要了。一、厨房墙砖哪种好?1、亚光砖……宁德时代年产能已达130GWh,动力电池毛利率降至23电车汇消息:宁德时代半年报显示,今年上半年实现营业总收入440。7亿元,同比增长134。07,归属于上市公司股东的净利润为44。8亿元,同比增长131。45。宁德时代主要……误导与欺诈客户,外汇经纪商ForexCT被澳洲法院重罚200澳大利亚联邦法院发布决议,向零售外汇经纪商ForexCapitalTradingPtyLtd(ForexCT)处以2000万澳元(约1549万美元)的罚款,该公司运营ASIC许……又一华为旗舰提前清仓,曾靠拍照一鸣惊人,如今降价依然抢手手机行业是高新技术行业,只有有深厚的技术积累再加上对于用户需求的理解,才能真正做出用户喜欢的产品。而华为正是凭借着强大的技术实力,转化成用户可感知的产品,最终塑造了自己的核心竞……乔布斯完美到变态?1块玻璃要做到300万,最贵的动一下就损失相信最近老美的动荡大家也都是有目共睹的,各个品牌的零售店都遭到了不同程度的损失,而苹果零售店更是格外严重,多台iPhone、iPad和Macbook等苹果产品被抢走,甚至有网友……读未来如何艰难,你要尽力而为马化腾有感本来觉得觉得是本励志的图书,主要马化腾的,毕竟是中国首富,从最早的OICQ,到微信,网游帝国,以及腾讯投资的公司如下:1、美团点评市值2万亿港币。腾讯为第一大股东,持股2……浦发推出绿色主题信用卡,践行低碳生活在2020年第七十五届联合国大会上,中国向世界郑重承诺力争在2030年前实现碳达峰,努力争取在2060年前实现碳中和。2021年政府工作报告中,扎实做好碳达峰、碳中和各项工作被……跑分100万的电脑仅击败86的全国用户?300元450主板之前挖的坑继续填还是那张价格爆炸的微星B450芯片组的主机板型号B450MAPROMAX价格爆炸供电给力32MBIOS支援所有AMD锐龙处理器,无……黑暗之魂深度解析乌拉席露与亚尔特留斯宫崎英高的黑魂,或者说宫崎英高的所有游戏,DLC都是一个极其重要的剧情补充,这个补充往往能够揭露出来整个故事里最大隐藏的谜团。比如《血源诅咒》老猎人这个DLC,揭露出来,……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网