马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
BiMap
Map 可以实现 key -> value 的映射,假如想要 value -> key 的映射,就需要定义两个 Map,并且同步更新,很不优雅。Guava 提供了 BiMap 支持支持双向的映射关系,常用实现有HashMap, EnumBiMap, EnumHashBiMap...。
而它对key和value严格的保证唯一性。假如使用put方法添加雷同的value值或key值则会抛出异常:java.lang.IllegalArgumentException,假如使用forcePut方法添加则会覆盖掉原来的value值。- BiMap<String, Integer> biMap = HashBiMap.create();
- biMap.put("A", 100);
- // 删除已存在的 KV,重新添加 KV
- biMap.forcePut("A", 200);
- // 获取反向映射
- BiMap<Integer, String> inverse = biMap.inverse();
- log.debug("{}", inverse.get(100));
复制代码 这里主要使用HashBiMap 举行分析
成员变量
- private static final double LOAD_FACTOR = 1.0D;
- // BiEntry是HashBiMap中为的Map.Entry接口的实现类,这里定义了两个BiEntry,一个是实现使用Key找到value的,另一个是实现使用value找到key的
- private transient HashBiMap.BiEntry<K, V>[] hashTableKToV;
- private transient HashBiMap.BiEntry<K, V>[] hashTableVToK;
- private transient int size;
- private transient int mask;
- private transient int modCount;
- private transient BiMap<V, K> inverse;
复制代码 HashMap做的是唯一key值对应的value可以不唯一,而Bimap做的是唯一key值,value值也要唯一,方便从key找到value,从value找到key- private static final class BiEntry<K, V> extends ImmutableEntry<K, V> {
- //key的hash值
- final int keyHash;
- //value的hash值
- final int valueHash;
- @Nullable
- //为key链表做的指向下一个节点的变量
- HashBiMap.BiEntry<K, V> nextInKToVBucket;
- @Nullable
- //为value链表做的指向下一个节点的变量
- HashBiMap.BiEntry<K, V> nextInVToKBucket;
- BiEntry(K key, int keyHash, V value, int valueHash) {
- super(key, value);
- this.keyHash = keyHash;
- this.valueHash = valueHash;
- }
- }
复制代码 对比一下HashMap的Node源码:- static class Node<K,V> implements Map.Entry<K,V> {
- //因为HashMap实现的功能只需要key找到value,所以这里的hash值默认就是key的hash值
- final int hash;
- final K key;
- V value;
- //在HashMap中的链表只做key的链表就好,所以只需要一个指向下一个节点的变量
- Node<K,V> next;
- }
复制代码 构造方法
- //传入期望容器长度
- private HashBiMap(int expectedSize) {
- this.init(expectedSize);
- }
复制代码 可以看到构造方法是私有的,以是在类中一定会有静态方法构造器会用到这个私有的构造方法。
这个构造方法调用了init方法,可以看一下init方法的源码:- private void init(int expectedSize) {
- CollectPreconditions.checkNonnegative(expectedSize, "expectedSize");
- //经过closedTableSize方法运算达到期望的实际值
- int tableSize = Hashing.closedTableSize(expectedSize, 1.0D);
- //初始化key和value存储链表的数组
- this.hashTableKToV = this.createTable(tableSize);
- this.hashTableVToK = this.createTable(tableSize);
- //初始化mask为数组最大小标值
- this.mask = tableSize - 1;
- //初始化modCount值为0
- this.modCount = 0;
- //初始化size值为0
- this.size = 0;
- }
复制代码 静态方法构造器
- public static <K, V> HashBiMap<K, V> create() {
- //调用另一个create构造器,期望长度为16
- return create(16);
- }
- public static <K, V> HashBiMap<K, V> create(int expectedSize) {
- //直接创建一个长度为expectedSize的HashBiMap
- return new HashBiMap(expectedSize);
- }
- public static <K, V> HashBiMap<K, V> create(Map<? extends K, ? extends V> map) {
- //创建一个与传入map相同长度的biMap
- HashBiMap bimap = create(map.size());
- //然后将传入map的值全部赋值给新的BiMap
- bimap.putAll(map);
- return bimap;
- }
复制代码 Multiset的接口中方法的实现在AbstractMapBasedMultiset抽象类中,下面针对AbstractMapBasedMultiset类的存储数据结构。add、remove、count和迭代器的实现举行分析
存储数据结构
- public V put(@Nullable K key, @Nullable V value) {
- return this.put(key, value, false);
- }
- public V forcePut(@Nullable K key, @Nullable V value) {
- return this.put(key, value, true);
- }
复制代码 构造方法
- private V put(@Nullable K key, @Nullable V value, boolean force) {
- //获取传入key的hash值
- int keyHash = hash(key);
- //获取传入value的hash值
- int valueHash = hash(value);
- //根据key的值和它的hash值查找是否存在这个节点,seekByKey方法就是遍历了这个keyhash所映射的下标上的链表进行查找。
- HashBiMap.BiEntry oldEntryForKey = this.seekByKey(key, keyHash);
- if(oldEntryForKey != null && valueHash == oldEntryForKey.valueHash && Objects.equal(value, oldEntryForKey.value)) {
- //如果这个key值存在,并且value也相等,则返回这个value
- return value;
- } else {
- //使用seekByValue查找这个value是否存在
- HashBiMap.BiEntry oldEntryForValue = this.seekByValue(value, valueHash);
- if(oldEntryForValue != null) {
- //如果存在,则判断force(第三个参数)是否为false
- if(!force) {//Value已经存在了,因此要判断是否允许强制插入
- //如果force(第三个参数)为false
- //则直接抛出异常
- String newEntry1 = String.valueOf(String.valueOf(value));
- throw new IllegalArgumentException((new StringBuilder(23 + newEntry1.length())).append("value already present: ").append(newEntry1).toString());
- }
- //如果force(第三个参数)为true,则删除这个节点,这个方法是双向删除
- this.delete(oldEntryForValue);
- }
- //如果key存在,则删除这个节点
- if(oldEntryForKey != null) {
- this.delete(oldEntryForKey);
- }
- //根据key,value,keyHash,valueHash创建一个BiEntry
- HashBiMap.BiEntry newEntry = new HashBiMap.BiEntry(key, keyHash, value, valueHash);
- //插入这个节点。
- this.insert(newEntry);
- //插入完成,刷新一下,看看是否需要扩容
- this.rehashIfNecessary();
- return oldEntryForKey == null?null:oldEntryForKey.value;
- }
- }
复制代码 add方法
- private void insert(HashBiMap.BiEntry<K, V> entry) {
- //计算出这个节点在key容器中的下标位置
- int keyBucket = entry.keyHash & this.mask;
- //使当前节点的keynext指向当前下标位置上
- entry.nextInKToVBucket = this.hashTableKToV[keyBucket];
- //将当前节点赋值给这个下标位置
- this.hashTableKToV[keyBucket] = entry;
-
- //value如key一样
- int valueBucket = entry.valueHash & this.mask;
- entry.nextInVToKBucket = this.hashTableVToK[valueBucket];
- this.hashTableVToK[valueBucket] = entry;
- //size加1
- ++this.size;
- ++this.modCount;
- }
复制代码 Count方法
- // 列表实现
- ListMultimap<String, Integer> listMultimap = MultimapBuilder.hashKeys().arrayListValues().build();
- // 集合实现
- SetMultimap<String, Integer> setMultimap = MultimapBuilder.treeKeys().hashSetValues().build();
- listMultimap.put("A", 1);
- listMultimap.put("A", 2);
- listMultimap.put("B", 1);
- // {A=[1, 2], B=[1, 2]}
- log.debug("{}", listMultimap);
- // [1, 2],不存在则返回一个空集合
- log.debug("{}", listMultimap.get("A"));
- // [1, 2] 移除 key 关联的所有 value
- List<Integer> valList = listMultimap.removeAll("A");
- // 返回普通 map 的视图,仅支持 remove,不能 put,且会更新原始的 listMultimap
- Map<String, Collection<Integer>> map = listMultimap.asMap();
复制代码 迭代器
- public static <K, V> HashMultimap<K, V> create() {
- //new一个HashMultimap,不传入任何值
- return new HashMultimap();
- }
- public static <K, V> HashMultimap<K, V> create(int expectedKeys, int expectedValuesPerKey) {
- //new一个HashHultimap,传入两个值,一个是期望key的长度,另一个是期望value的长度
- return new HashMultimap(expectedKeys, expectedValuesPerKey);
- }
- public static <K, V> HashMultimap<K, V> create(Multimap<? extends K, ? extends V> multimap) {
- //传入一个Multimap值
- return new HashMultimap(multimap);
- }
复制代码 Multiset中有一个实现了Iterator接口的类:
[code]private class MapBasedMultisetIterator implements Iterator { final Iterator entryIterator; java.util.Map.Entry currentEntry; int occurrencesLeft; boolean canRemove; MapBasedMultisetIterator() { //获取当前map容器的迭代器 this.entryIterator = AbstractMapBasedMultiset.this.backingMap.entrySet().iterator(); } //根据当前迭代器判断是否另有元素 public boolean hasNext() { return this.occurrencesLeft > 0 || this.entryIterator.hasNext(); } public E next() { //假如occurrencesLeft为0,则说明现在处于刚开始,或上一个元素完成 if(this.occurrencesLeft == 0) { //迭代器向下获取一个元素 this.currentEntry = (java.util.Map.Entry)this.entryIterator.next(); //获取到当前元素的个数 this.occurrencesLeft = ((Count)this.currentEntry.getValue()).get(); } //因为是获取一个元素,以是减去这一个 --this.occurrencesLeft; this.canRemove = true; return this.currentEntry.getKey(); } public void remove() { CollectPreconditions.checkRemove(this.canRemove); int frequency = ((Count)this.currentEntry.getValue()).get(); if(frequency |