前言
这个库简化了你的代码,使它易写、易读、易于维护。它能提高你的工作效率,让你从大量重复的底层代码中脱身。
虽然JDK提供给我们的集合框架已经足够强大,基本能解决我们平时的绝大所述问题,并且效率还挺高。
本文针对于Apache提供的Collections4组件提供的一些特殊数据结构,通过例子解决一些实际问题的讲解。
® bag接口
® 固定大小的map、lru (最近最少使用算法)map和双重(dual)map
® 对象数组和map的迭代器
® map的multikey
® 大量的工具类,提供了使用api的快捷方式
® 封装器,对大多数类提供了自定义的方法
Bag
Bag继承自Collection接口,定义了一个集合,该集合会记录对象在集合中出现的次数。
假设你有一个包,包含{a, a, b, c}。调用getCount(a)方法将返回2,调用uniqueset()方法将返回{a, b, c}的set集合。
public interface Bag<E> extends Collection<E> {}
顾名思义,它是包的意思,所以也是拿来装数据的。
HashBag
HashBag使用HashMap
作为数据存储,是一个标准的
Bag实现。
public static void main(String[] args) { Bag hashBag = new HashBag(); String s1 = "s1"; String s2 = "s2"; hashBag.add(s1); hashBag.add(s1); //一次性放置多个元素 hashBag.add(s2, 3); // 获得包中元素迭代器 Iterator<?> iterator = hashBag.iterator(); System.out.println("包中元素为:"); while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("包中元素个数为:" + hashBag.size()); //5 //下面两个特有的方法 使用起来较为方便 System.out.println("包中entity1个数为:" + hashBag.getCount(s1)); //2 System.out.println("去重后个数为:" + hashBag.uniqueSet().size()); //2 } 结果输出: 包中元素为: s1 s1 s2 s2 s2 包中元素个数为:5 包中entity1个数为:2 去重后个数为:2
TreeBag
TreeBag使用TreeMap作为数据存储,用法与HashBag类似,只是TreeBag会使用自然顺序对元素进行排序。
总结
使用的方式和List差不多,效果也大同小异。
场景:比如我们需要具体知道每个元素出现的次数的时候,并且实现快速去重,使用Bag会非常便捷
对应的BagUtils,能提供BagUtils.EMPTY_BAG、synchronizedBag、unmodifiableBag等编程同步、只读的快捷方法
BidiMap: 双重Map
使用双向映射
,可以使用值查找键,并且可以使用键轻松查找值。(自然,它可以根绝key移除,也可以根据value移除)
该场景使用还是比较多的,比如一对一的映射关系,都可以使用这来存储。如果你使用HashMap,那你得维护两个,还是比较麻烦的
public interface BidiMap<K, V> extends IterableMap<K, V> {}
也是个普通的Map。继承IterableMap增加了一种迭代方式,例子里会有讲解
DualHashBidiMap
底层维护两个HashMap,一个正向,一个逆向来达到效果的。
public DualHashBidiMap() { super(new HashMap<K, V>(), new HashMap<V, K>()); } //把一个普通的Map转成BidiMap public DualHashBidiMap(final Map<? extends K, ? extends V> map) { super(new HashMap<K, V>(), new HashMap<V, K>()); putAll(map); }
看个示例:
public static void main(String[] args) { BidiMap<String, String> map = new DualHashBidiMap<>(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); //多出来的一种遍历方式 还是分厂人性化的 MapIterator<String, String> it = map.mapIterator(); while (it.hasNext()) { it.next(); //此句话必须调用 返回的是key,效果同getKey,但必须调用 System.out.println(it.getKey() + "---" + it.getValue()); } System.out.println(map.get("key1")); //value1 //根据value拿key System.out.println(map.getKey("value1")); //key1 //这个方法是Map接口的 System.out.println(map.getOrDefault("k", "defaultValue")); //defaultValue //返回一个逆序的视图 注意是视图 BidiMap<String, String> inverseMap = map.inverseBidiMap(); //根据key删除 inverseMap.remove("key1"); //根据value删除 inverseMap.removeValue("value2"); System.out.println(map); //{key1=value1, key2=value2, key3=value3} System.out.println(inverseMap); //{value2=key2, value1=key1, value3=key3} } 输出: key1---value1 key2---value2 key3---value3 value1 key1 defaultValue {key1=value1, key2=value2, key3=value3} {value2=key2, value1=key1, value3=key3}
DualLinkedHashBidiMap
底层采用两个LinkedHashMap存储,其余同上
DualTreeBidiMap
底层采用两个TreeMap存储,其余同上
它不要求key和value都是实现了比较器接口的,但是自己可以自定义比较器接口传进去
TreeBidiMap
注意TreeBidiMap和DualTreeBidiMap的区别
TreeBidiMap采用是红黑树:Node。一个node就是put的一个键值对,这样子来实现双端的Map,底层的原理和上面的不一样。这样的好处:可以最大程度的节约存储空间,从而提高效率。
firstKey、lastKey、nextKey等等都有一套自己的实现,处理效率还是蛮高的
备注:使用起来基本同上,因此实例省略
此Map要求key和value必须必须必须都实现了比较器接口
MultiKeyMap:多键Map
MultiKeyMap能够解决我们平时可能遇到的一个痛点。
比如我们Map的key,可能是由多个字段的值联合决定的(有点类似联合索引的意思),这个时候我们一般方案为:自己拼接字符串,然后put进去。
但现在有了MultiKeyMap,我们可以非常优雅的解决这个问题:
public static void main(String[] args) { // MultiKey功能很简单:装载多个key的一个对象 MultiKey<String> multiKey = new MultiKey<>("a", "b"); System.out.println(multiKey); //MultiKey[a, b] MultiKeyMap<String, String> multiKeyMap = new MultiKeyMap(); // 多个键对应一个值 两个key:name和NAME multiKeyMap.put("name", "NAME", "jianggujin"); System.out.println(multiKeyMap); //{MultiKey[name, NAME]=jianggujin} System.out.println(multiKeyMap.get("name")); //null System.out.println(multiKeyMap.get("NAME")); //null System.out.println(multiKeyMap.get("name", "NAME")); //jianggujin //测试key覆盖 multiKeyMap.put("name", "shixiang", "cover"); System.out.println(multiKeyMap); //{MultiKey[name, shixiang]=cover, MultiKey[name, NAME]=jianggujin} //这样子 value值才会被覆盖 multiKeyMap.put("name", "NAME", "cover"); System.out.println(multiKeyMap); //{MultiKey[name, shixiang]=cover, MultiKey[name, NAME]=cover} }
我们可以看到 name+NAME联合确定了一个value值。这样子,我们就可以非常优雅的处理这种情况,并且还不容易犯错。
MultiKeyMap底层采用MultiKey作为普通Map的key,采用HashedMap存储
HashedMap:
源码解释:
* A <code>Map</code> implementation that is a general purpose alternative * to <code>HashMap</code>. * <p> * This implementation improves on the JDK1.4 HashMap by adding the * {@link org.apache.commons.collections4.MapIterator MapIterator} * functionality and many methods for subclassing.
简单的说就是做了一个HashMap的通用替代品。让也能使用IterableMap的迭代器那样去使用和迭代Map了,没什么多余的可以说明的。