程序员的福音 - Apache Commons Collecti
此文是系列文章第九篇,前几篇请点击链接查看
程序员的福音 - Apache Commons Compress
Apache Commons Collections 是对 java.util.Collection 的扩展。
目前 Collections 包有两个 commons-collections 和commons-collections4,commons-collections 最新版本是3.2.2,不支持泛型,目前官方已不在维护。collections4 目前最新版本是4.4,最低要求 Java8 以上。相对于 collections 来说完全支持 Java8 的特性并且支持泛型,该版本无法兼容旧有版本,于是为了避免冲突改名为 collections4。推荐直接使用该版本。(注:两个版本可以共存,使用时需要注意)
包结构如下:
org.apache.commons.collections4
org.apache.commons.collections4.bag
org.apache.commons.collections4.bidimap
org.apache.commons.collections4.collection
org.apache.commons.collections4.comparators
org.apache.commons.collections4.functors
org.apache.commons.collections4.iterators
org.apache.commons.collections4.keyvalue
org.apache.commons.collections4.list
org.apache.commons.collections4.map
org.apache.commons.collections4.multimap
org.apache.commons.collections4.multiset
org.apache.commons.collections4.properties
org.apache.commons.collections4.queue
org.apache.commons.collections4.sequence
org.apache.commons.collections4.set
org.apache.commons.collections4.splitmap
org.apache.commons.collections4.trie
maven坐标如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
由于其功能较多,下面挑几个有代表的简单介绍一下用法。
01. 工具类
1. CollectionUtils
String str = null;
List list1 = Arrays.asList(new String[]{"1", "2", "3"});
List list2 = Arrays.asList(new String[]{"1", "2", "4"});
// 判断是否为空(null或空list都为true)
CollectionUtils.isEmpty(list1);
// 添加元素(忽略null元素)
CollectionUtils.addIgnoreNull(list1, str);
// list是否包含subList中的所有元素
CollectionUtils.containsAll(list1, list2); // false
// list是否包含subList中的任意一个元素
CollectionUtils.containsAny(list1, list2); // true
// list1 减去 list2
CollectionUtils.subtract(list1, list2); // ["3"]
// 合并两个list并去重
CollectionUtils.union(list1, list2); //["1", "2", "3", "4"]
// 取两个list同时存在的元素
CollectionUtils.intersection(list1, list2); // [1", "2"]
2. ListUtils
List list1 = Arrays.asList(new String[]{"1", "2", "3"});
List list2 = Arrays.asList(new String[]{"1", "2", "4"});
// 同CollectionUtils, 返回结果为List
ListUtils.subtract(list1, list2); // ["3"]
ListUtils.union(list1, list2); //["1", "2", "3", "4"]
ListUtils.intersection(list1, list2); // [1", "2"]
// 判断两个集合中的内容是否完全相同(顺序也一致)
ListUtils.isEqualList(list1, list2); // false
// list1如果为null则转换为空List
ListUtils.emptyIfNull(list1);
// list1中所有元素做Hash
ListUtils.hashCodeForList(list1);
除了以上介绍了两个还有 MapUtils,SetUtils,EnumerationUtils,IterableUtils 等不是很常用就不多做介绍了。
02. 集合扩展
1. FixedSizeList
FixedSizeList 用于装饰另一个 List 以阻止修改其大小。不支持添加、删除、清除等操作。set 方法是允许的(因为它不会改变列表大小),下面看代码示例
List<String> sourceList = new ArrayList<>();
sourceList.add("1");
// 装饰一下原list
List<String> list = FixedSizeList.fixedSizeList(sourceList);
list.set(0, "11");
println(list); // [11,2,3]
// 以下改变容器size的操作会抛出异常
list.add("4"); // UnsupportedOperationException("List is fixed size")
list.remove("5"); // UnsupportedOperationException("List is fixed size")
list.clear(); // UnsupportedOperationException("List is fixed size")
2. SetUniqueList
SetUniqueList 用来装饰另一个 List 以确保不存在重复元素,内部使用了 Set 来判断重复问题
List<String> sourceList = new ArrayList<>();
sourceList.add("1");
sourceList.add("2");
// 元素不重复的list
SetUniqueList<String> list = SetUniqueList.setUniqueList(sourceList);
// 存在则不处理,不会影响原来顺序
list.add("2");
println(list); // [1,2]
3. TransformedList
TransformedList 装饰另一个 List 以转换添加的对象。add 和 set 方法受此类影响。
List<String> sourceList = new ArrayList<>();
sourceList.add("1");
sourceList.add("2");
// 转换list,在添加元素的时候会通过第二个参数Transformer转换一下
// (Transformer接口只有一个抽象方法可以使用lambda表达式)
// transformingList不会对原list的已有元素做转换
TransformedList<String> list = TransformedList.transformingList(sourceList, e -> e.concat("_"));
list.add("a");
println(list); // [1, 2, a_]
// transformedList会对原list的已有元素做转换
list = TransformedList.transformedList(sourceList, e -> e.concat("_"));
list.add("a");
println(list); // [1_, 2_, a_]
4. PredicatedList
PredicatedList 装饰另一个 List ,装饰后的 List 在添加元素的时候会调用 Predicate 接口来判断元素,匹配通过才会被添加到集合中。
List<String> sourceList = new ArrayList<>();
// 在添加元素的时候会通过第二个参数Predicate判断一下是否符合要求,符合要求才添加进来
PredicatedList<String> list = PredicatedList.predicatedList(new ArrayList<>(), e -> e.startsWith("_"));
list.add("_4");
println(list); // [_4]
// 以下会抛异常:java.lang.IllegalArgumentException: Cannot add Object '4'
list.add("4");
5. ListOrderedSet
ListOrderedSet 有序的Set,顺序按照元素添加顺序排列,类似 List
// 有序的set,按照插入顺序排序
Set<String> set = new ListOrderedSet<>();
set.add("aa");
set.add("11");
set.add("哈哈");
println(set); // [aa,11,哈哈]
6. Bag
Bag 接口是带计数功能的集合扩展,它继承了 Collection 接口,可以当做集合来使用
// bag 带计数功能的集合
Bag<String> bag = new HashBag<>();
bag.add("a");
bag.add("b");
bag.add("a");
println(bag.size()); // 3
println(bag.getCount("a")); // 2
集合扩展除了以上列举的几个外,还有很多就不做介绍了,感兴趣的可以自行翻阅源码研究。
03. Map扩展
1. MultiValuedMap
MultiValuedMap 和正常的 Map 有点区别,同一个 key 允许存放多个 value,这些 value 会放到一个 List 中。这个功能如果用 Java 的 Map 我们需要构造一个 Map<String, List<String>> 加个各种操作来实现
// list实现,允许value重复
ListValuedMap<String, String> map = new ArrayListValuedHashMap<>();
map.put("user", "张三");
map.put("user", "李四");
map.put("user", "张三");
map.put("age", "12");
// 注意:value的泛型是String, 但是get方法返回的是List<String>
List<String> users2 = map.get("user"); // [张三,李四,张三]
// multiMap的其他方法
map.containsKey("user"); // true
map.containsValue("张三"); // true
map.containsMapping("user", "张三"); // true
int size = map.size(); // 4
Collection<String> ss = map.values();// [张三,李四,张三,12]
map.remove("user"); // 清空user的所有value
// 转换为原生map
Map<String, Collection<String>> jMap = map.asMap();
2. CaseInsensitiveMap
key大小写不敏感的Map
// key大小写不敏感
Map<String, Integer> map = new CaseInsensitiveMap<>();
map.put("one", 1);
map.put("two", 2);
Integer o = map.get("ONE");
println(o); // 1
3. OrderedMap
有顺序的 Map,按照插入顺序排序。如果使用 hashMap 的话 key 会按照 hash 值排序,可能和插入顺序一样,也可能不一样。key 数量和不同 JDK 版本都可能影响顺序,这是由于不同版本 jdk map 的 hash 算法有区别,hash 算法和当前 map 的容量也有关系。
// key有序:按照插入顺序
OrderedMap<String, String> map = new ListOrderedMap<>();
map.put("哈哈", "1");
map.put("此处", "2");
map.put("cc", "3");
map.put("dd", "4");
// 得到的keySet有序
Set<String> set = map.keySet(); // 哈哈,此处,cc,dd
String nk = map.nextKey("此处"); // cc
String pk = map.previousKey("此处"); // 哈哈
4. LRUMap
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
各种缓存框架都有对 LRU 算法的支持,如 EhCache,GuavaCache,Redis 等,可以说是很常用的一种算法
LRUMap<String, String> map = new LRUMap<>(2);
map.put("aa", "1");
map.put("bb", "2");
map.put("cc", "3");
// 最早没有被使用的aa将被移出
println(map); // [bb:2, cc:3]
// 访问一次bb,此时在put的话将会移出最早没有被访问的cc
map.get("bb");
map.put("dd", "4");
println(map); // [bb:2, dd:4]
5. PassiveExpiringMap
装饰一个 Map 以在达到过期时间时删除过期条目。当在 Map 中放置键值对时,此装饰器使用 ExpirationPolicy 来确定条目应保持多长时间,由到期时间值定义。当对 Map 做操作的时候才会检查元素是否过期并触发删除操作。
// 存活一秒钟
int ttlMillis = 1000;
PassiveExpiringMap.ExpirationPolicy<String, String> ep = new PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<>(ttlMillis);
PassiveExpiringMap<String, String> map = new PassiveExpiringMap<>(ep);
map.put("a", "1");
map.put("b", "2");
map.put("c", "3");
// 等待一秒后在获取
Thread.sleep(1000);
String vc = map.get("c");
println(vc); // null
6. ReferenceMap
ReferenceMap 允许垃圾收集器删除映射。可以指定使用什么类型的引用来存储映射的键和值。如果使用的不是强引用,则垃圾收集器可以在键或值变得不可访问,或者 JVM 内存不足时删除映射。用它做一个简易的缓存不会导致存放内容过多导致内存溢出。
关于 Java 的引用类型主要四种 强引用,软引用,弱引用,虚引用。平时我们基本用的都是强引用,关于这四种引用类型不是本文介绍的重点,如果不了解的话可以自行搜索进行学习。
下面直接看 ReferenceMap 代码示例
// key value全部使用软引用,再JVM内存不足的情况下GC会将软引用的键值对回收
ReferenceMap<String, String> map = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.SOFT, AbstractReferenceMap.ReferenceStrength.SOFT);
// 其他map操作 ... ...
7. BidiMap
BidiMap 允许在 key 和 value 之间进行双向查找。其中一个键可以查找一个值,一个值可以同样轻松地查找一个键。这个接口扩展了 Map,value 不允许重复,如果重复将同时覆盖旧的键值对。
// 双向map, 可通过value获取key
// value也不允许重复,如果重复将会覆盖旧值
BidiMap<String, String> map = new TreeBidiMap<>();
map.put("dog", "狗");
map.put("cat", "猫");
// value重复的话key也会被覆盖,相当于"cat2:猫"会覆盖掉"cat:猫"
// map.put("cat2", "猫");
println(map); // {cat=猫, dog=狗}
String key = map.getKey("狗");
println(key); // dog
// 反向,value变为key,key变为value
BidiMap<String, String> iMap = map.inverseBidiMap();
println(iMap); // {狗=dog, 猫=cat}
println(iMap.get("狗")); // dog
// 对反向map操作同时影响原map
iMap.put("鱼", "fish");
println(iMap); // {狗=dog, 猫=cat, 鱼=fish}
println(map); // {cat=猫, dog=狗, fish=鱼}
Map 扩展除了刚才介绍了几种外还有一些扩展限于篇幅就不做介绍了
05. 总结
Java 集合 和 Map 可以说是非常常用的数据结构了, Commons Collections 做为 Java 集合的扩展,增加了很多功能各异的集合 和 Map,对于实现一些特殊的需求来说是很方便的。如果有对应的需求可以考虑使用。
后续章节我将继续给大家介绍 commons 中其他好用的工具类库,期待你的关注。