IT评测·应用市场-qidao123.com技术社区
标题:
【Guava】BiMap&Multimap&Multiset
[打印本页]
作者:
拉不拉稀肚拉稀
时间:
2025-4-1 07:10
标题:
【Guava】BiMap&Multimap&Multiset
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
欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/)
Powered by Discuz! X3.4