My logo
Published on

Java-Guava 使用

1. Google 的 Guava组件

public class GuavaUtilTest {

    public static void main(String[] args) {
        //getTableDemo();
        //biMapDemo();
        //multimapDemo();
        //rangeMapDemo();
        //classToInstanceMapDemo();

        //splitterDemo();
        //charMatcherDemo();

        //arrayDemo();
        //collectionDemo();
        //functionDemo();
        //predicateDemo();
        //checkedDemo("name", 12, Maps.newHashMap());

        cacheDemo();
        executorDemo();
    }

    // JDK中提供了Future/FutureTask/Callable来对异步回调进行支持,但是还是看上去挺复杂的,能不能更加简单呢?比如注册一个监 听回调
    // 我们可以通过 我 guava对JDK提供的线程池进行装饰,让其具有异步回调监听功能,然后在设置监听器即可!
    private static void executorDemo() {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // guava 封装的带有监听回调功能的线程池
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);
        ListenableFuture<Integer> listenableFuture = listeningExecutorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                if (new Random().nextInt(3) == 2) {
                    throw new NullPointerException();
                }
                return 1;
            }
        });

        FutureCallback futureCallback = new FutureCallback<Integer>() {
            @Override
            public void onSuccess(Integer integer) {
                System.out.println("----------" + integer);
            }

            @Override
            public void onFailure(Throwable throwable) {
                System.out.println("----------" + throwable.getMessage());
            }
        };

        //Futures.addCallback(listenableFuture, futureCallback);
    }

    // 定义缓存的实现
    private static void cacheDemo() {
        CacheLoader<Long, UserData> cacheLoader = new CacheLoader<Long, UserData>() {

            @Override
            public UserData load(Long aLong) throws Exception {
                // 模拟从数据库,redis 缓存中加载数据
                UserData userData = new UserData();
                userData.setId(aLong);
                return userData;
            }
        };

        // 定义缓存的策略,提供对外的访问缓存
        CacheBuilder.newBuilder()
                .expireAfterAccess(2, TimeUnit.SECONDS)
                .expireAfterWrite(2, TimeUnit.SECONDS)
                .maximumSize(10000L)
                .build(cacheLoader);
    }

    private static void checkedDemo(String name, int age, HashMap<Object, Object> map) {
        Preconditions.checkNotNull(name, "name must");
        Preconditions.checkArgument(age >= 18, "age must");
        for (Map.Entry<Object, Object> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

    // 断言 Predicate最常用的功能就是运用在集合的过滤当中
    private static void predicateDemo() {
        ArrayList<String> list = Lists.newArrayList("helloworld", "yes", "zhangsanfeng");
        Collections2.filter(list, new Predicate<String>() {
            @Override
            public boolean apply(String s) {
                // 业务逻辑
                return new StringBuilder(s).reverse().toString().equals(s);
            }
        });

        for (String s : list) {
            System.out.println(s);
        }
    }

    // 函数编程
    private static void functionDemo() {
        ArrayList<String> list = Lists.newArrayList("helloworld", "yes", "zhangsanfeng");
        Function<String, String> f1 = new Function<String, String>() {

            @Override
            public String apply(String s) {
                return s.length() <= 5 ? s : s.substring(0, 5);
            }
        };

        Function<String, String> f2 = new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        };

        // 函数式编程的好处在于在集合遍历操作中提供自定义 函 Function的操作,比如 的 transform转换。我们再也不需要一遍遍的遍历集合
        Function<String, String> f3 = Functions.compose(f1, f2);
        Collection<String> transform = Collections2.transform(list, f3);
        for (String s : transform) {
            System.out.println(s);
        }
    }

    // guava对JDK提供的集合类型的补充
    private static void collectionDemo() {
        List<String> list = Lists.newArrayList();
        list.add("a");
        list.add("b");

        // 保护性拷贝
        List<String> readList = Collections.unmodifiableList(new ArrayList<>(list));
        //readList.add("c");

        // guava  处理
        ImmutableList<String> immutable = ImmutableList.of("a", "b", "c");
        //immutable.add("d");   Exception in thread "main" java.lang.UnsupportedOperationException

        // 就一个copyOf
        ImmutableList<String> immutable2 = ImmutableList.copyOf(list);
        list.add("d");
        System.out.println("list size:" + list.size() + "immutable2.size:" + immutable2.size());

        // ImmutableMap的例子
        ImmutableBiMap<String, String> immutableBiMap = ImmutableBiMap.of("name", "zhangsan", "sex", "man");
        immutableBiMap.put("wife", "no...");
    }

    // 数组的操作 guava对JDK提供的原生类型操作进行了扩展
    private static void arrayDemo() {
        List<Integer> list = Ints.asList(1, 3, 5, 7, 9);
        System.out.println(Ints.join("&", 1, 3, 1, 4));

        // 原生类型数组快速合并
        int[] newIntArray = Ints.concat(new int[]{1, 2}, new int[]{2, 3, 4});
        System.out.println(newIntArray.length);

        // 最大/最小
        System.out.println(Ints.max(newIntArray) + "," + Ints.min(newIntArray));

        // 是否包含
        System.out.println(Ints.contains(newIntArray, 9));

        // 集合到数组的转换
        int[] ints = Ints.toArray(list);
    }

    // CharMatcher,将字符的匹配和处理解耦
    private static void charMatcherDemo() {

        final CharMatcher charMatcherDigit = CharMatcher.digit();
        final CharMatcher charMatcherAny = CharMatcher.any();

        // 只是保留匹配的字符 其他移除
        System.out.println(charMatcherDigit.retainFrom("abc2def134f~"));

        // 移除匹配的字符
        System.out.println(charMatcherDigit.removeFrom("yes, i love u 1314"));

        System.out.println(charMatcherAny.inRange('a', 'f').or(charMatcherAny.is('n')).replaceFrom("zhangfengzhe", "*"));
    }

    /**
     * 对于对 Joiner,常用的方法是 , 跳过跳 NULL元素: 元 skipNulls() / 对于对 NULL元素使用其他替代: 元 useForNull(String)
     * 对 Splitter,常用的方法是: , trimResults()/omitEmptyStrings()。注意拆分的方式,有字符串,还有正则,还有固定长度分割
     */
    private static void splitterDemo() {
        // 连接器
        final Joiner joiner = Joiner.on(",").skipNulls();
        // 分割器
        final Splitter splitter = Splitter.on(",").trimResults().omitEmptyStrings();

        //把集合/数组中的元素join在一起
        String join = joiner.join(Lists.newArrayList("a", null, "b"));
        System.out.println("join=" + join);

        for (String tmp : splitter.split("a, ,b,,")) {
            System.out.println("[" + tmp + "]");
        }
    }

    // ClassToInstanceMap 是一个比较特殊的 Map ,它的键是 Class ,而值是这个 Class 对应的实例对象。
    private static void classToInstanceMapDemo() {

        ClassToInstanceMap<Object> instanceMap = MutableClassToInstanceMap.create();
        User user = new User("Hydra", 18);
        Dept dept = new Dept("develop", 200);
        instanceMap.putInstance(User.class, user);
        instanceMap.putInstance(Dept.class, dept);

        // 这样是可以正常执行的,因为 HashMap 和 TreeMap 都集成了 Map 父类,但是如果想放入其他类型
        ClassToInstanceMap<Map> instanceMap2 = MutableClassToInstanceMap.create();
        HashMap<String, Object> hashMap = new HashMap<>();
        TreeMap<String, Object> treeMap = new TreeMap<>();
        ArrayList<Object> list = new ArrayList<>();
        instanceMap2.putInstance(HashMap.class, hashMap);
        instanceMap2.putInstance(TreeMap.class, treeMap);
    }

    // RangeMap - 范围
    private static void rangeMapDemo() {
        TreeRangeMap<Comparable, Object> rangeMap = TreeRangeMap.create();
        // 先后创建了 [0,60) 的左闭右开区间、 [60,90] 的闭区间、 (90,100] 的左开右闭区间
        rangeMap.put(Range.closedOpen(0, 60), "fail");
        rangeMap.put(Range.closed(60, 90), "satisfactory");
        rangeMap.put(Range.openClosed(90, 100), "excellent");
        System.out.println(rangeMap.get(59));
        System.out.println(rangeMap.get(60));
        System.out.println(rangeMap.get(90));
        System.out.println(rangeMap.get(91));

        // 当然我们也可以移除一段空间,下面的代码移除了 [70,80] 这一闭区间后,再次执行 get 时返回结果为 null
        rangeMap.remove(Range.closed(70, 80));
        System.out.println(rangeMap.get(75));
    }

    // guava中的 Multimap 提供了将一个键映射到多个值的形式,使用起来无需定义复杂的内层集合,可以像使用普通的 Map 一样使用
    private static void multimapDemo() {
        ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
        multimap.put("day", 1);
        multimap.put("day", 2);
        multimap.put("day", 8);
        multimap.put("month", 3);
        // {month=[3], day=[1, 2, 8]}
        System.out.println(multimap);

        // 获取值的集合
        // 在上面的操作中,创建的普通 Multimap 的 get(key) 方法将返回一个 Collection 类型的集合
        Collection<Integer> day = multimap.get("day");
        // 如果在创建时指定为 ArrayListMultimap 类型,那么 get 方法将返回一个 List
        List<Integer> dayList = multimap.get("day");

        // Multimap 的 get 方法会返回一个非 null 的集合,但是这个集合的内容可能是空
        List<Integer> day3 = multimap.get("day");
        List<Integer> year3 = multimap.get("year");
        System.out.println(day3);
        System.out.println(year3);

        // 操作get后的集合 和 BiMap 的使用类似,使用 get 方法返回的集合也不是一个独立的对象,可以理解为集合视图的关联,对这个新集合的操作仍然会 作用于原始的 Multimap 上
        ArrayListMultimap<String, Integer> multimap2 = ArrayListMultimap.create();
        multimap2.put("day", 1);
        multimap2.put("day", 2);
        multimap2.put("day", 8);
        multimap2.put("month", 3);
        List<Integer> day2 = multimap2.get("day");
        List<Integer> month2 = multimap2.get("month");
        day.remove(0);//这个0是下标
        month2.add(12);
        System.out.println(multimap2);

        // 转换Map 使用 asMap 方法,可以将 Multimap 转换为 Map<K,Collection> 的形式,同样这个 Map 也可以看做一个关联的视图,在这 个 Map 上的操作会作用于原始的 Multimap
        Map<String, Collection<Integer>> map = multimap.asMap();
        for (String key : map.keySet()) {
            System.out.println(key + " : " + map.get(key));
        }
        map.get("day").add(20);
        System.out.println(multimap);

        // Multimap 中的数量在使用中也有些容易混淆的地方
        System.out.println(multimap.size());
        System.out.println(multimap.entries().size());
        for (Map.Entry<String, Integer> entry : multimap.entries()) {
            System.out.println(entry.getKey() + "," + entry.getValue());
        }
    }

    // 双向Map
    private static void biMapDemo() {
        // ### 在普通 Map 中,如果要想根据 value 查找对应的 key ,没什么简便的办法,无论是使用 for 循环还是迭代器,都需要遍历整个map
        HashBiMap<String, String> biMap = HashBiMap.create();
        biMap.put("Hydra", "Programmer");
        biMap.put("Tony", "IronMan");
        biMap.put("Thanos", "Titan");

        //使用key获取value
        System.out.println(biMap.get("Tony"));
        BiMap<String, String> inverse = biMap.inverse();
        //使用value获取key
        System.out.println(inverse.get("Titan"));

        // ⚠️ 反转后操作的影响 上面我们用 inverse 方法反转了原来 BiMap 的键值映射,但是这个反转后的 BiMap 并不是一个新的对象,
        // 它实现了一种视图的关 联,所以对反转后的 BiMap 执行的所有操作会作用于原先的 BiMap 上。
        HashBiMap<String, String> biMap2 = HashBiMap.create();
        biMap2.put("Hydra", "Programmer");
        biMap2.put("Tony", "IronMan");
        biMap2.put("Thanos", "Titan");
        BiMap<String, String> inverse2 = biMap.inverse();
        inverse.put("IronMan", "Stark");
        System.out.println(biMap2);
        // 由于 BiMap 的 value 是不允许重复的,因此它的 values 方法返回的是没有重复的 Set ,而不是普通 Collection
        Set<String> values = biMap.values();

        // value不可重复 BiMap 的底层继承了 Map ,我们知道在 Map 中 key 是不允许重复的,
        // 而双向的 BiMap 中 key 和 value 可以认为处于等价地位, 因此在这个基础上加了限制, value 也是不允许重复的。
        HashBiMap<String, String> biMap3 = HashBiMap.create();
        biMap3.put("Tony", "IronMan");
        biMap3.put("Stark", "IronMan");  // 会抛出一个 IllegalArgumentException 异常
        // 如果你非想把新的 key 映射到已有的 value 上,那么也可以使用 forcePut 方法强制替换掉原有的 key
        biMap3.forcePut("Stark", "IronMan");
    }

    /**
     * java中的 Map 只允许有一个 key 和一个 value 存在,
     * 但是guava中的 Table 允许一个 value 存在两个 key 。
     * Table 中的两 个 key 分别被称为 rowKey 和 columnKey ,也就是行和列。
     */
    private static void getTableDemo() {
        Table<String, String, Integer> table = HashBasedTable.create();
        // ###存放元素
        table.put("Hydra", "Jan", 20);
        table.put("Hydra", "Feb", 28);

        table.put("Trunks", "Jan", 28);
        table.put("Trunks", "Feb", 16);

        // ###取出元素
        Integer dayCount = table.get("Hydra", "Feb");
        System.out.println(dayCount);

        //rowKey或columnKey的集合
        Set<String> rowKeySets = table.rowKeySet();
        Set<String> columnKeySets = table.columnKeySet();

        //value集合
        Collection<Integer> values = table.values();

        // 上面打印它们的结果, key 的集合是不包含重复元素的, value 集合则包含了所有元素并没有去重
        System.out.println("rowKeySets:" + rowKeySets);
        System.out.println("columnKeySets:" + columnKeySets);
        System.out.println("values:" + values);

        // ### 计算 key对应的所有 value的和
        for (String key : rowKeySets) {
            Set<Map.Entry<String, Integer>> rows = table.row(key).entrySet();
            int total = 0;
            for (Map.Entry<String, Integer> row : rows) {
                total += row.getValue();
            }
            System.out.println(key + ": " + total);
        }

        // ### 转换rowKey和columnKey
        Table<String, String, Integer> transpose = Tables.transpose(table);
        Set<Table.Cell<String, String, Integer>> cells = transpose.cellSet();
        cells.forEach(cell -> {
            System.out.println(cell.getRowKey() + "," + cell.getColumnKey() + ":" + cell.getValue());
        });

        // ### 转换为嵌套的Map 下面2 种方式都可以
        Map<String, Map<String, Integer>> rowMap = table.rowMap();
        Map<String, Map<String, Integer>> columnMap = table.columnMap();
    }

    private static class User {
        public User(String hydra, int i) {
        }
    }

    private static class Dept {
        public Dept(String develop, int i) {
        }
    }

    @Data
    private static class UserData {
        private Long id;
    }
}