Guava中的这些Map操作将我的代码大小减少了50%。I/O和许多其他方面。一方面,使用这些API可以简化我们的代码,让代码更加优雅。另一方面补充了很多jdk中没有的功能,可以让我们的开发更有效率。Hydra今天要给大家分享的是Guava封装Map的一些风骚操作。用完这些功能,不得不说一句很贴心的话。先引入依赖坐标,然后开始我们正式的体验~com.google.guavaguava30.1.1-jre复制代码Table-双键Mapjava中的Map只允许一键一值存在,而Guava中的Table允许一个值有两个键。Table中的两个键分别叫做rowKey和columnKey,即row和column。(但是我个人觉得把它们理解为行和列不是很准确,把它们看成两列可能更合适。)举个简单的例子,如果你想记录一个员工工作的天数每一个月。如果用java中的普通Map实现,需要嵌套两层:Map>map=newHashMap<>();//storeelementMapworkMap=newHashMap<>();workMap.put("Jan",20);workMap.put("Feb",28);map.put("Hydra",workMap);//获取元素IntegerdayCount=map.get("Hydra").get("Jan");如果使用Table,复制代码很简单,看一下简化代码:Tabletable=HashBasedTable.create();//storeelementtable.put("Hydra","Jan",20);table.put("Hydra","Feb",28);table.put("Trunks","Jan",28);table.put("Trunks","Feb",16);//获取元素IntegerdayCount=table.get("Hydra","Feb");复制代码我们不需要构建复杂的两层Map,直接做就可以了。除了元素访问,我们再看看其他的实际操作。1.获取key或value的集合//rowKey或columnKey集合SetrowKeys=table.rowKeySet();SetcolumnKeys=table.columnKeySet();//value集合Collectionvalues=表.值();复制代码以分别打印它们的结果。键集不包含重复元素,值集包含所有没有去重的元素:[Hydra,Trunks][Jan,Feb][20,28,28,16]复制代码2.计算所有值的和对应key以所有rowKey对应的values之和为例:for(Stringkey:table.rowKeySet()){Set>rows=table.row(key).entrySet();inttotal=0;for(Map.Entryrow:rows){total+=row.getValue();}System.out.println(key+":"+total);}复制代码打印结果:Hydra:48Trunks:44复制代码3.转换rowKey和columnKey的操作也可以理解为行和列的转置,直接调用的静态方法transpose表格:Tabletable2=Tables.transpose(table);Set>cells=table2.cellSet();cells.forEach(cell->System.out.println(cell.getRowKey()+","+cell.getColumnKey()+":"+cell.get价值()));复杂的系统代码使用cellSet方法获取所有数据行,打印结果,可以看到行和列已经对调了:Jan,Hydra:20Feb,Hydra:28Jan,Trunks:28Feb,Trunks:16复制代码4,转换成嵌入式你还记得在使用Table之前存储数据的格式吗?如果想把数据恢复成嵌套Map的形式,可以使用Table的rowMap或者columnMap方法:Map>rowMap=table.rowMap();Map>columnMap=table.columnMap();复制代码查看转换后的Map中的内容,分别按行和列汇总:{Hydra={Jan=20,Feb=28},Trunks={Jan=28,Feb=16}}{Jan={Hydra=20,Trunks=28},Feb={Hydra=28,Trunks=16}}复制代码BiMap-BidirectionalMap在normalMap中,如果想根据value找到对应的key,没有简单的方法。无论使用for循环还是迭代器,都需要遍历整个Map以循环keySet的方式为例:publicListfindKey(Mapmap,Stringval){Listkeys=newArrayList<>();for(Stringkey:map.keySet()){if(map.get(key).equals(val))keys.add(key);}returnkeys;}复制代码,guava中的BiMap提供了一个key和value双向关联的数据结构,首先看一个简单的例子:HashBiMapbiMap=HashBiMap.create();biMap.put("Hydra","Programmer");biMap.put("Tony","IronMan");biMap.put("Thanos","Titan");//使用key获取valueSystem.out.println(biMap.get("Tony"));BiMapinverse=biMap.inverse();//使用value获取获取密钥系统。out.println(inverse.get("泰坦"));复制代码执行结果:IronManThanos复制代码看起来很实用,不是吗?不过在使用中还是有一些需要避免的坑,下面会一一梳理。1.反转后操作的影响上面我们使用了反转的方法对原BiMap的key-value映射进行反转,但是反转后的BiMap并不是一个新的对象,它实现了一种视图关联,所以反转所有后面的BiMap执行的操作将应用于原始BiMap。HashBiMapbiMap=HashBiMap.create();biMap.put("九头蛇","程序员");biMap.put("托尼","钢铁侠");biMap.put("灭霸","泰坦"");BiMapinverse=biMap.inverse();inverse.put("钢铁侠","斯塔克");System.out.println(biMap);将代码复制到反向BiMap中的内容修改后,查看BiMap中原来的内容:{Hydra=Programmer,Thanos=Titan,Stark=IronMan}复制代码可以看到原来对应的key钢铁侠的价值在于托尼。虽然没有直接修改过,但是现在钥匙变成了Stark。2.值不能重复。BiMap底层继承了Map。我们知道Map中的key是不允许重复的,双向BiMap中的key和value可以认为是在一个等价的位置。所以在这个基础上加了限制,值也是不允许重复的。看看下面的代码:HashBiMapbiMap=HashBiMap.create();biMap.put("Tony","IronMan");biMap.put("Stark","IronMan");复制像这样的代码如果不能正常结束,会抛出IllegalArgumentException:如果硬要将新的key映射到已有的值,也可以使用forcePut方法强制替换原key:HashBiMapbiMap=HashBiMap.create();biMap.put("Tony","IronMan");biMap.forcePut("Stark","IronMan");复制代码,打印替换后的BiMap:{Stark=IronMan}复制代码,顺便多说一句,由于BiMap的值不允许重复,所以它的values方法返回一个不重复的Set,而不是普通的Collection:Setvalues=biMap.values();复制代码Multimap-多值MapjavaMap维护着键值一对一的关系。如果要将一个键映射到多个值,只能将值的内容设置为一个集合。简单实现如下:Map>map=newHashMap<>();Listlist=newArrayList<>();list.add(1);list.add(2);map.put("天",列表);guava中的Multimap提供了一种将键映射到多个值的形式。它不需要定义复杂的内部集合。它可以像普通地图一样使用。定义和输入数据如下:Multimapmultimap=ArrayListMultimap.create();multimap.put("day",1);multimap.put("day",2);multimap.put("天",8);multimap.put("月",3);复制代码打印这个Multimap的内容,可以直观的看出每个key对应一个集合:{month=[3],day=[1,2,8]}1.获取values的集合中以上操作,创建的普通Multimap的get(key)方法会返回一个Collection类型的集合:Collectionday=multimap.get("day");如果在创建时复制代码如果指定了ArrayListMultimap类型,则get方法将返回一个List:ArrayListMultimapmultimap=ArrayListMultimap.create();Listday=multimap.get("day");可以创建HashMultimap、TreeMultimap等类型的MultimapMultimap的get方法会返回一个非null的集合,但是这个集合的内容可能是空的,看下面的例子:Listday=multimap.get("day");Listyear=多图。get("year");System.out.println(day);System.out.println(year);复制代码打印结果:[1,2,8][]复制代码2,集合和BiMap在操作get之后的使用类似,get方法返回的集合并不是一个独立的对象。可以理解为集合视图的关联。对这个新集合的操作仍然会作用于原来的Multimap。看看下面的例子:ArrayListMultimapmultimap=ArrayListMultimap.create();multimap.put("day",1);multimap.put("day",2);multimap.put("day",8);multimap.put("month",3);Listday=multimap.get("day");Listmonth=multimap.get("month");day.remove(0);//这个0是下标月份。添加(12);System.out.println(多图);复制代码查看修改后的结果:{month=[3,12],day=[2,8]}复制代码3.使用asMap方法转换为Map,Multimap可以转换为Map,而这个Map也可以看做是一个关联视图,对这个Map的操作会应用到原来的Multimap上。Map>map=multimap.asMap();for(Stringkey:map.keySet()){System.out.println(key+":"+map.get(key));}map.get("day").add(20);System.out.println(multimap);复制代码执行结果:month:[3]day:[1,2,8]{month=[3],day=[1,2,8,20]}复制代码4.数量问题Multimap中的数量也是使用上有些迷惑,先看下面的例子:Integer>entry:multimap.entries()){System.out.println(entry.getKey()+","+entry.getValue());}复制代码打印结果:44month,3day,1day,2day,8copycode这是因为size()方法返回的是所有key到单个value的映射,所以结果为4,entries()方法同理,返回key和单个value的键值对集合。但是它的keySet存储了不同键的数量。例如,下面这行代码打印的结果将是2.System.out.println(multimap.keySet().size());复制代码看看转成Map,数量会变:Set>>entries=multimap.asMap().entrySet();System.out.println(entries.尺寸());复制代码代码运行结果为2,因为获取了key到Collection的映射关系。RangeMap-RangeMap首先看一个例子,假设我们要根据分数对考试分数进行分类,那么代码中就会有这么丑陋的if-else:publicstaticStringgetRank(intscore){if(0<=score&&score<60)return"fail";elseif(60<=score&&score<=90)return"satisfactory";elseif(90rangeMap=TreeRangeMap.create();rangeMap.put(Range.closedOpen(0,60),"fail");rangeMap.put(Range.closed(60,90),"满意");rangeMap.put(Range.openClosed(90,100),"优秀");System.out.println(rangeMap.get(59));System.out.println(rangeMap.get(60));System.out.println(rangeMap.get(90));System.out.println(rangeMap.get(91));复制代码上面代码中,已经创建了[0,60)左闭右开区间,[60,90]的闭区间,(90,100]的左开右闭区间分别是分别映射到某个值。打印运行结果:failsatisfactorysatisfactoryexcellent复制代码当然我们也可以去掉一段空格。以下代码去掉闭区间[70,80]后,再次执行get返回结果为null:rangeMap.remove(Range.closed(70,80));System.out.println(rangeMap.get(75));复制代码ClassToInstanceMap-InstanceMapClassToInstanceMap是一个特殊的Map,它的key是Class,value是这个Class对应的实例对象。让我们看一个使用putInstance方法存储对象的简单示例:ClassToInstanceMap