目录
一、简介
二、环境搭建
三、Table - 双键Map
四、BiMap - 双向Map
五、Multimap - 多值Map
六、RangeMap - 范围Map
七、ClassToInstanceMap - 实例Map
一、简介
(1)Guava是google公司开发的一款Java类库扩展工具包,内含了丰富的API,涵盖了集合、缓存、并发、I/O等多个方面。
(2)这些AP简化代码,使代码更为优雅,另一方面它补充了很多jdk中没有的功能,能让我们开发中更为高效。
二、环境搭建
// 1.新建一个maven项目
// 2.引入maven
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
三、Table - 双键Map
#1.定义
(1)双key单value:java中的Map只允许有一个key和一个value存在,但是guava中的Table允许一个value存在两个key。分别被称为rowKey和columnKey,也就是行和列。(其实也可以看作两列)
(2)单层优势:不需要再构建复杂的双层Map<String,Map<String,Integer>>,直接一层搞定Table<String,String,Integer>
(3)values未去重:key(rowKey和columnKey)的集合是不包含重复元素的,value集合则包含了所有元素并没有去重
(4)行列转置:Tables的静态方法transpose
(5)转换为原始嵌套map:table.rowMap()
#2.代码演示
package com.fong.doublekey;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import org.junit.Test;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class DoubleKeyMap {
// 2.方法二: guava用法 记录每个员工每个月工作的天数
@Test
public void new1() {
Table<String, String, Integer> table = HashBasedTable.create();
// 存放元素
table.put("fong", "Jan", 27);
table.put("fong", "Feb", 28);
table.put("wong", "Jan", 30);
table.put("wong", "Feb", 31);
// 取出元素
Integer dayCount = table.get("fong", "Jan");
System.out.println("员工fong在Jan月上班的天数为: " + dayCount);
// ============================value集合(未去重)========================================
System.out.println("\n=========================value集合(未去重)==========================");
// rowKey或columnKey的集合
Set<String> rowKeys = table.rowKeySet(); // [fong, wong]
Set<String> columnKeys = table.columnKeySet();// [Jan, Feb]
// value集合(未去重)
Collection<Integer> values = table.values(); // [28, 29, 30, 31]
System.out.println(rowKeys);
System.out.println(columnKeys);
System.out.println(values);
// ==============================统计员工所有上班总数======================================
System.out.println("\n=========================统计员工所有上班总数============================");
for (String empName : table.rowKeySet()) {
Set<Map.Entry<String, Integer>> entries = table.row(empName).entrySet();
int dayCount1 = 0;
for (Map.Entry<String, Integer> entry : entries) {
dayCount1 += entry.getValue();
}
System.out.println(empName + "上班总天数为: " + dayCount1);
}
// ==============================行和列的转置transpose, row和column发生互换=============================
System.out.println("\n=====================行和列的转置transpose, row和column发生互换========================");
Table<String, String, Integer> transposeTable = Tables.transpose(table);
Set<Table.Cell<String, String, Integer>> cellSet = transposeTable.cellSet();
cellSet.forEach(
cell -> System.out.println(cell.getRowKey() + " " + cell.getColumnKey() + " " + cell.getValue())
);
// ==============================转换为嵌套map=============================
System.out.println("\n=====================转换为嵌套map========================");
Map<String, Map<String, Integer>> rowMap = table.rowMap();
Map<String, Map<String, Integer>> columnMap = table.columnMap();
System.out.println(rowMap);
System.out.println(columnMap);
}
// 1.方法一: 记录每个员工每个月工作的天数
@Test
public void old() {
Map<String, Map<String, Integer>> map = new HashMap<>();
// 存放元素
Map<String, Integer> workMap = new HashMap<>();
workMap.put("Jan", 20);
workMap.put("Feb", 28);
map.put("fong", workMap);
// 取出元素
Integer dayCount = map.get("fong").get("Jan");
System.out.println("员工fong在Jan月上班的天数为: " + dayCount);
}
}
四、BiMap - 双向Map
#1.定义
(1)免遍历实现通过value查找key: Map想根据value查找对应的key,无论是使用for循环还是迭代器,都需要遍历整个Map, BiMap提供了一种key和value双向关联的数据结构, 可以解决这个问题
(2)inverse()方法后的影响: inverse方法反转了原来BiMap的键值映射,但反转后的BiMap并不是一个新的对象,它仅仅是了一种视图的关联,所以对反转后的BiMap执行的所有操作会作用于原先的BiMap上。
(3)value不可重复: Map中key是不允许重复的, BiMap的底层继承了Map, BiMap中key和value可以认为处于等价地位,因此在这个基础上加了限制,value也是不允许重复的
注意: 你非想把新的key映射到已有的value上,那么也可以使用forcePut方法强制替换掉原有的key
(4)values返回Set: BiMap的value是不允许重复的,因此它的values方法返回的是没有重复的Set,而不是普通Collection
#2.代码演示
package com.fong.bimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 根据value --> 找key, 并不需要遍历Map
*/
public class BiMapTest {
// 2.方法二: guava中的BiMap提供了一种key和value双向关联的数据结构
@Test
public void new1() {
HashBiMap<String/*员工名*/, String/*职业*/> biMap = HashBiMap.create();
// 存放元素
biMap.put("fong", "engineer");
biMap.put("wong", "doctor");
biMap.put("li", "nurse");
// 取出元素
BiMap<String, String> inverseBiMap = biMap.inverse();
System.out.println("职位为engineer的员工有: " + inverseBiMap.get("engineer")); // "fong"
// ======反转后的BiMap并不是新的对象,它是一种视图关联,反转后的BiMap执行的操作会作用于原先的BiMap上========
System.out.println("\n=====反转后的BiMap并不是新的对象,它是一种视图关联,反转后的BiMap执行的操作会作用于原先的BiMap上=====");
inverseBiMap.put("engineer", "fong先生");
// 原先值为engineer时对应的键是fong,虽然没有直接修改,但是现在键变成了fong先生
System.out.println(biMap);
// ==============================key不可重复, 并且value也不可重复!======================================
System.out.println("\n====Map中key是不允许重复的,而双向的BiMap中key和value处于等价地位,value也是不允许重复的====");
biMap.put("zhang1", "lawyer");
//biMap.put("zhang2", "lawyer"); // 这个会抛异常 ILlegalArgumentException
// ==============================若value也要更新, 可以强制覆盖=============================
System.out.println("\n=====================若value也要更新, 可以强制覆盖=========================");
biMap.forcePut("zhang2", "lawyer");
System.out.println(biMap);
// =================BiMap的value也是不可重复的, 所以values方法返回的Set, 而不是Collection===============
System.out.println("\n=================BiMap的value也是不可重复的, 所以values方法返回的Set, 而不是Collection===============");
Set<String> values = biMap.values(); // set
System.out.println(values);
}
// 1.方法一: 普通的map中, 若通过value找到key, 则必须要遍历map才能够实现
// guava中的BiMap提供了一种key和value双向关联的数据结构
public List<String> findKey(Map<String, String> map, String val){
List<String> keys = new ArrayList<>();
for (String key : map.keySet()) {
if (map.get(key).equals(val))
keys.add(key);
}
return keys;
}
}
五、Multimap - 多值Map
#1.定义
(1)单key多value: Map维护的是键值一对一的关系,如果要将一个键映射到多个值上,只能嵌套Map<Object, List<Object>>这种形式了
(2)常用类型: ArrayListMultimap.create(); HashMultimap、TreeMultimap等类型
(3)操作get后的集合: 和BiMap的使用类似,使用get方法返回的集合也不是一个独立的对象,可以理解为集合视图的关联,对这个新集合的操作仍然会作用于原始的Multimap上
(4)转换为嵌套Map: Multimap转换为Map<K,Collection>的形式, 同样作为视图的关联, 在这个Map上的操作会作用于原始的Multimap
(5)Multimap的数量问题
#2.代码演示
package com.fong.multimap;
import com.google.common.collect.ArrayListMultimap;
import org.junit.Test;
import java.util.*;
/**
* guava中的Multimap提供了将一个键映射到多个值的形式
*/
public class MultiMapTest {
@Test
public void new3() {
ArrayListMultimap<String/*月份*/, Integer/*当月请假日期(1-31)*/> arrayListMultimap = ArrayListMultimap.create();
// 存放元素
arrayListMultimap.put("Jan", 15);
arrayListMultimap.put("Feb", 1);
arrayListMultimap.put("Feb", 12);
arrayListMultimap.put("Feb", 31);
// ======multiMap的数量混淆问题======
System.out.println("\n======multiMap的数量问题======");
// size()方法返回的是所有key到单个value的映射 4
System.out.println(arrayListMultimap.size());
// entries()方法返回的是key 和 单个value的键值对集合 4
System.out.println(arrayListMultimap.entries().size());
// key的数量 2
System.out.println(arrayListMultimap.keySet().size());
// 转化为嵌套map 2
Set<Map.Entry<String, Collection<Integer>>> entries = arrayListMultimap.asMap().entrySet();
System.out.println(entries.size());
}
@Test
public void new2() {
ArrayListMultimap<String/*月份*/, Integer/*当月请假日期(1-31)*/> arrayListMultimap = ArrayListMultimap.create();
// 存放元素
arrayListMultimap.put("Jan", 15);
arrayListMultimap.put("Feb", 1);
arrayListMultimap.put("Feb", 12);
arrayListMultimap.put("Feb", 31);
// 取出元素是一个集合
List<Integer> febList = arrayListMultimap.get("Feb");
// ======get返回集合不是独立对象,理解为集合视图关联,对这个新集合操作仍然会作用于原始的Multimap上(类似BiMap)======
System.out.println("\n======get返回集合不是独立对象,理解为集合视图关联,对这个新集合操作仍然会作用于原始的Multimap上(类似BiMap)======");
System.out.println("febList修改前, arrayListMultimap:" + arrayListMultimap);
febList.remove(0); // 0是下标
febList.add(29);// 二月新增29号请假单
System.out.println("febList修改后, arrayListMultimap:" + arrayListMultimap);
// ======转换为map, 同样在这个map操作也会作用于原始的multiMap======
System.out.println("\n======转换为map, 同样在这个map操作也会作用于原始的multiMap======");
Map<String, Collection<Integer>> collectionMap = arrayListMultimap.asMap();
for (String key : collectionMap.keySet()) {
System.out.println(key+" : "+collectionMap.get(key));
}
collectionMap.get("Feb").add(20);
System.out.println(arrayListMultimap);
}
// 2.方法二: guava中的Multimap提供了将一个键映射到多个值的形式
@Test
public void new1() {
ArrayListMultimap<String/*月份*/, Integer/*当月请假日期(1-31)*/> arrayListMultimap = ArrayListMultimap.create();
// 存放元素
arrayListMultimap.put("Jan", 15);
arrayListMultimap.put("Feb", 1);
arrayListMultimap.put("Feb", 12);
arrayListMultimap.put("Feb", 31);
// 取出元素是一个集合
System.out.println("arrayListMultimap: " + arrayListMultimap);
System.out.println("arrayListMultimap.get(Feb): " + arrayListMultimap.get("Feb"));
// ======可以创建HashMultimap、TreeMultimap等类型的Multimap========
// ======Multimap的get方法会返回一个非null的集合,但是这个集合的内容可能是空========
System.out.println("======可以创建HashMultimap、TreeMultimap等类型的Multimap========");
System.out.println("======Multimap的get方法会返回一个非null的集合,但是这个集合的内容可能是空========");
List<Integer> febList = arrayListMultimap.get("Feb");
List<Integer> marchList = arrayListMultimap.get("March");
System.out.println("febList: " + febList);
System.out.println("marchList: " + marchList);
}
// 1.方法一: Map键值为一对一, 若要实现一个键映射多个值, 则需要内嵌List
// guava中的Multimap提供了将一个键映射到多个值的形式
@Test
public void old() {
Map<String, List<Integer>> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
map.put("day", list);
}
}
六、RangeMap - 范围Map
#1.定义
(1)例子:要根据分数对考试成绩进行分类,代码中就会出现这样丑陋的if-else
(2)RangeMap: guava中的RangeMap描述了一种从区间到特定值的映射关系, 即可以创建[0,60)的左闭右开区间、[60,90)的左闭右开区间、(90,100]的左开右闭区间,并分别映射到某个值上
#2.代码演示
package com.fong.rangemap;
import com.google.common.collect.Range;
import com.google.common.collect.TreeRangeMap;
import org.junit.Test;
/**
* 范围Map 例子: 成绩分类
*/
public class RangeMapTest {
// 2.方法二: 使用范围map
@Test
public void new1() {
TreeRangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closedOpen(0, 60), "fail");
rangeMap.put(Range.closedOpen(60, 90), "satisfactory");
rangeMap.put(Range.closed(90, 100), "excellent");
System.out.println(rangeMap.get(-1));
System.out.println(rangeMap.get(59));
System.out.println(rangeMap.get(60));
System.out.println(rangeMap.get(90));
System.out.println(rangeMap.get(100));
System.out.println(rangeMap.get(1000));
}
// 1.方法一: 这样的方式太多if else
public static String getRank(int score) {
if (0 <= score && score < 60)
return "fail";
else if (60 <= score && score <= 90)
return "satisfactory";
else if (90 < score && score <= 100)
return "excellent";
return null;
}
}
七、ClassToInstanceMap - 实例Map
#1.定义
(1)特殊key,value: 它的键是Class,而值是这个Class对应的实例对象,
(2)ClassToInstanceMap有啥好处? 不是用普通的map<Class, Object>就可以解决吗?
2.1强制类型转换:省去了复杂的强制类型转换
2.2ClassToInstanceMap接口的定义,它是带有泛型的: 可以严格限制put进去的类型, 起到约束作用
2.3想缓存对象,又不想做复杂的类型校验,那么使用方便的ClassToInstanceMap就可以了。
#2.代码演示
package com.fong.instanceMap;
import com.google.common.collect.MutableClassToInstanceMap;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* 实例Map, 键是class, 值为class对应实例
* 如果你想缓存对象,又不想做复杂的类型校验,那么使用方便的ClassToInstanceMap就可以了。
*/
public class InstanceMapTest {
// 2.方法二: 使用范围map
// 优点 (1)复杂的强制类型转换 (2)无法对类型进行限制
@Test
public void new2() {
// ========范围可以限制put的对象类型=======
// HashMap, TreeMap实现Map接口
MutableClassToInstanceMap<Map> instanceMap = MutableClassToInstanceMap.create();
HashMap<String, Object> hashMap = new HashMap<>();
TreeMap<String, Object> treeMap = new TreeMap<>();
ArrayList<Object> list = new ArrayList<>();
instanceMap.putInstance(HashMap.class, hashMap);
instanceMap.putInstance(TreeMap.class, treeMap);
// 编译检查都无法通过
//instanceMap.putInstance(ArrayList.class, treeMap);
}
@Test
public void new1() {
MutableClassToInstanceMap<Object> instanceMap = MutableClassToInstanceMap.create();
User user = new User("fong", 22);
Dept dept = new Dept(1234, "研发部");
instanceMap.put(User.class, user);
instanceMap.put(Dept.class, dept);
System.out.println(instanceMap);
// 打印了true,说明了取出的确实是我们之前创建并放入的那个对象
System.out.println(instanceMap.getInstance(User.class) == user);
}
// 1.方法一: Map<Class,Object>也可以实现
// 缺点 (1)复杂的强制类型转换 (2)无法对类型进行限制
public static void old(int score) {
Map<Class, Object> map = new HashMap<>();
User user = new User("fong", 18);
Dept dept = new Dept(200, "developDept");
map.put(User.class, user);
map.put(Dept.class, dept);
}
}