美文网首页
程序员的福音 - Apache Commons Collecti

程序员的福音 - Apache Commons Collecti

作者: 菜鸟码农的Java笔记 | 来源:发表于2021-08-05 23:17 被阅读0次

    此文是系列文章第九篇,前几篇请点击链接查看

    程序猿的福音 - Apache Commons简介

    程序员的福音 - Apache Commons Lang

    程序员的福音 - Apache Commons IO

    程序员的福音 - Apache Commons Codec

    程序员的福音 - Apache Commons Compress

    程序员的福音 - Apache Commons Exec

    程序员的福音 - Apache Commons Email

    程序员的福音 - Apache Commons Net

    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 中其他好用的工具类库,期待你的关注。

    相关文章

      网友评论

          本文标题:程序员的福音 - Apache Commons Collecti

          本文链接:https://www.haomeiwen.com/subject/ijnvvltx.html