【小家java】Java之Apache Commons-Collections4使用精讲(Bag、Map、List、Set全覆盖)(上)

简介: 【小家java】Java之Apache Commons-Collections4使用精讲(Bag、Map、List、Set全覆盖)(上)

前言


这个库简化了你的代码,使它易写、易读、易于维护。它能提高你的工作效率,让你从大量重复的底层代码中脱身。


虽然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了,没什么多余的可以说明的。



相关文章
|
3月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
271 1
|
4月前
|
存储 JavaScript 前端开发
for...of循环在遍历Set和Map时的注意事项有哪些?
for...of循环在遍历Set和Map时的注意事项有哪些?
266 121
|
4月前
|
存储 C++ 容器
unordered_set、unordered_multiset、unordered_map、unordered_multimap的介绍及使用
unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。在unordered_set中,元素的值同时也是唯一地标识它的key。在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。它的迭代器至少是前向迭代器。前向迭代器的特性。
189 0
|
4月前
|
编译器 C++ 容器
用一棵红黑树同时封装出map和set
再完成上面的代码后,我们的底层代码已经完成了,这时候已经是一个底层STL的红黑树了,已经已符合库里面的要求了,这时候我们是需要给他穿上对应的“衣服”,比如穿上set的“衣服”,那么这个穿上set的“衣服”,那么他就符合库里面set的要求了,同样map一样,这时候我们就需要实现set与map了。因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。我们就可以使用仿函数了。
50 0
|
4月前
|
存储 编译器 容器
set、map、multiset、multimap的介绍及使用以及区别,注意事项
set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列。set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重。set默认是升序的,但是其内部默认不是按照大于比较,而是按照小于比较。set中的元素不能被修改,因为set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树。
202 0
|
7月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
184 2
|
8月前
|
编译器 容器
哈希表模拟封装unordered_map和unordered_set
哈希表模拟封装unordered_map和unordered_set
|
8月前
|
编译器 测试技术 计算机视觉
红黑树模拟封装map和set
红黑树模拟封装map和set
|
10月前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
177 20

热门文章

最新文章