guava工具类Map

目录
一、简介
二、环境搭建
三、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);
    }
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容