HashBasedTable(双键Map)
双键数据结构类型,底层由LinkedHashMap
实现,官方定义可以将两个Key
分别示意为rowKey
和columnKey
,且提供了按rowKey
获取或者按columnKey
获取的API,注意无论是rowKey、columnKey
还是value
都不能为null
值。
HashBasedTable
实际上本身也是HashMap<R, HashMap<C,V>>
的结构,因此,rowKey
的查找性能要高于columnKey
的查找。
public static void table() {
HashBasedTable<String, String, String> table = HashBasedTable.create();
// put操作
table.put("研发部", "张三", "20");
table.put("研发部", "李四", "21");
table.put("产品部", "王五", "22");
table.put("销售部", "赵六", "23");
table.put("行政部", "小明", "24");
// 按rowKey和columnKey获取value
String value = table.get("研发部", "张三");
// 输出结果:20
System.out.println(value);
// 按columnKey获取,rowKey和value就分别为Map中的Key和Value
Map<String, String> columnKey = table.column("张三");
// 输出结果:{研发部=20}
System.out.println(columnKey);
// 按rowKey获取,columnKey和value就分别为Map中的Key和Value
Map<String, String> rowKey = table.row("研发部");
// 输出结果:{张三=20, 李四=21}
System.out.println(rowKey);
// 以column为Key,转成原始的Map套Map的格式
Map<String, Map<String, String>> toColumnMap = table.columnMap();
// 输出结果:{张三={研发部=20}, 李四={研发部=21}, 王五={产品部=22}, 赵六={销售部=23}, 小明={行政部=24}}
System.out.println(toColumnMap);
// 以row为Key,转成原始的Map套Map的格式
Map<String, Map<String, String>> toRowMap = table.rowMap();
// 输出结果:{研发部={张三=20, 李四=21}, 产品部={王五=22}, 销售部={赵六=23}, 行政部={小明=24}}
System.out.println(toRowMap);
// 按columnKey转Set集合
Set<String> toColumnKeySet = table.columnKeySet();
// 输出结果:[张三, 李四, 王五, 赵六, 小明]
System.out.println(toColumnKeySet);
// 按rowKey转Set集合
Set<String> toRowKeySet = table.rowKeySet();
// 输出结果:[研发部, 产品部, 销售部, 行政部]
System.out.println(toRowKeySet);
// 按values转成集合
Collection<String> values = table.values();
// 输出结果:[20, 21, 22, 23, 24]
System.out.println(values);
// 各种contains判断
table.containsColumn("");
table.containsRow("");
table.containsValue("");
table.contains("", "");
}
内部实际上也是Map套Map
的结构
public static <R, C, V> HashBasedTable<R, C, V> create() {
return new HashBasedTable<>(new LinkedHashMap<R, Map<C, V>>(), new Factory<C, V>(0));
}
执行put
操作时,也是先判断rowKey
是否存在于LinkedHashMap
中,不存在则put
,存在则直接取出,然后再put -> columnKey
public V put(R rowKey, C columnKey, V value) {
checkNotNull(rowKey);
checkNotNull(columnKey);
checkNotNull(value);
return getOrCreate(rowKey).put(columnKey, value);
}
`factory`实际上也是一个`LinkedHashMap`
private Map<C, V> getOrCreate(R rowKey) {
Map<C, V> map = backingMap.get(rowKey);
if (map == null) {
map = factory.get();
backingMap.put(rowKey, map);
}
return map;
}
类似的,除了HashBasedTable
之外,也有支持排序的TreeBasedTable
,以及不可变的ImmutableTable
,同样他们都不是线程安全的,使用时应当注意。
HashBiMap(双向查找Map)
由两个Hash
表组成,不但支持由Key
查找Value
,还支持由Value
查找Key
,同时Key
和Value
都可以为null
值
public static void biMap() {
HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("研发部", "张三");
// 由于Key和Value可以互相转换,所以正常情况下Value不能重复
// biMap.put("产品部", "张三"); 会报错,也可以强制替换,biMap.forcePut("产品部", "张三")
biMap.get("研发部");
BiMap<String, String> inverse = biMap.inverse();
inverse.get("张三");
inverse.put("产品部", "李四");
// 输出结果:李四
System.out.println(inverse.get("产品部"));
// 输出结果:{研发部=张三, 李四=产品部}
System.out.println(biMap);
// 输出结果:{张三=研发部, 产品部=李四}
System.out.println(inverse);
}
由于需要需要两张Hash
表,所以每次put
时,除了完成正向Map
的put
之外,还需完成反向Map
的put
。
根据拿到正向Map
的Key
对应的Value
,再做为反向Map
的Key
填入
private void insert(BiEntry<K, V> entry, @CheckForNull BiEntry<K, V> oldEntryForKey) {
int keyBucket = entry.keyHash & mask;
entry.nextInKToVBucket = hashTableKToV[keyBucket];
hashTableKToV[keyBucket] = entry;
int valueBucket = entry.valueHash & mask;
entry.nextInVToKBucket = hashTableVToK[valueBucket];
hashTableVToK[valueBucket] = entry;
if (oldEntryForKey == null) {
entry.prevInKeyInsertionOrder = lastInKeyInsertionOrder;
entry.nextInKeyInsertionOrder = null;
if (lastInKeyInsertionOrder == null) {
firstInKeyInsertionOrder = entry;
} else {
lastInKeyInsertionOrder.nextInKeyInsertionOrder = entry;
}
lastInKeyInsertionOrder = entry;
} else {
entry.prevInKeyInsertionOrder = oldEntryForKey.prevInKeyInsertionOrder;
if (entry.prevInKeyInsertionOrder == null) {
firstInKeyInsertionOrder = entry;
} else {
entry.prevInKeyInsertionOrder.nextInKeyInsertionOrder = entry;
}
entry.nextInKeyInsertionOrder = oldEntryForKey.nextInKeyInsertionOrder;
if (entry.nextInKeyInsertionOrder == null) {
lastInKeyInsertionOrder = entry;
} else {
entry.nextInKeyInsertionOrder.prevInKeyInsertionOrder = entry;
}
}
size++;
modCount++;
}
双向查找的Map
,同样也有不可变的ImmutableBiMap
,使用双向查找Map
时由于同时维护了两个Map
,并且正反操作的都是同一个对象,这就导致在实际使用时会非常容易出错,因此建议慎用!
TreeRangeMap(范围Map)
基于RangeMap
和TreeMap
实现,Key
和Value
都不能为null
,可以用于替换if...else,switch
等条件语句
private static void rangeMap() {
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closedOpen(0, 18), "未成年");
rangeMap.put(Range.closed(18, 30), "青年");
rangeMap.put(Range.openClosed(30, 60), "中年");
// 输出结果:青年
System.out.println(rangeMap.get(18));
// 输出结果:{[0..18)=未成年, [18..30]=青年, (30..60]=中年}
System.out.println(rangeMap.asMapOfRanges());
// 输出结果:[18..30]=青年
System.out.println(rangeMap.getEntry(20));
// 输出结果:[0..60]
System.out.println(rangeMap.span());
}
TreeRangeMap
的外层是一个TreeMap
,其中Key
记录着Range
的左区间,Value
是一个RangeMap
。
@Override
public void put(Range<K> range, V value) {
if (!range.isEmpty()) {
checkNotNull(value);
remove(range);
entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value));
}
}
基于TreeMap
是有序性,直接通过floorEntry
找到小于等于Key
的最大映射
@Override
@CheckForNull
public V get(K key) {
Entry<Range<K>, V> entry = getEntry(key);
return (entry == null) ? null : entry.getValue();
}
@Override
@CheckForNull
public Entry<Range<K>, V> getEntry(K key) {
Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry =
entriesByLowerBound.floorEntry(Cut.belowValue(key));
if (mapEntry != null && mapEntry.getValue().contains(key)) {
return mapEntry.getValue();
} else {
return null;
}
}