Guava中的一些加强集合类

写了好多和Java集合类有关的文章,学习了好多集合类的用法,有没有感受仍是有一些常见的需求集合类没有办法知足呢?须要本身使用Java集合中的类去实现,可是这种经常使用的轮子Google和apache都帮咱们造好啦.java

Java相关的工具包中有两个颇有名,Google GuavaApache Commons,今天就来看一下Guava中实现的一些其余的集合类,基本上都是在JDK的集合类上作了一些加强.apache

Immutable Collections -> 真正的不可修改的集合

在上文Java Collections中,提到了Collections类中提供了一些能够返回集合不可变视图的方法,咱们如今来试用一下.数据结构

咱们新建一个包含5个元素的list.而后建立一个它的不可变视图.ide

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5));
        List<Integer> umList = Collections.unmodifiableList(list);
复制代码

通过上面的步骤,咱们拿到了umList,他是不可变的,可是list没有消失,它仍然是可变的,咱们能够经过给list添加一个值来改变umList的元素集.工具

由于在Collections.unmodifiableList中,持有了一个list的引用,全部对list的更改也会同步体如今umList上.学习

并且上面的代码中,多了一个中间变量list.由于咱们不须要它,咱们建立它只是为了获取后面的不可变集合.比较繁琐.ui

使用Guava怎么作呢?像下面这样:this

ImmutableCollection li = ImmutableList.of(1, 2, 3, 4, 5);

复制代码

是否是感受清晰了不少,同时,这个li是真正的不可变的.编码

ImmutableList还提供了多种建立的方法,好比:spa

  1. 使用任意个元素的参数直接建立.
  2. 使用copyOf从一个已有的List来建立
  3. 提供Builder模式来进行链式调用.

上面的代码以ImmutableList举例,可是Guava还提供了ImmutableSet,ImmutableMap,ImmutableCollection等类,能够根据须要的数据结构分别调用.

MultiMap -> Map<Strinh,List>的另外一种解决办法

咱们常常会有一种需求,须要在Map结构里面存List/Set.

好比,统计用户的签到日期用来画日历,那么咱们想要的数据是:name->[2019-04-01,2019-04-28]这样子的数据结构.

那么咱们先加入一个用户在5月1号的签到记录怎么办呢?写一会儿代码,

// 模拟已有的数据结构
    static Map<String, List<String>> userSign = new HashMap<>();
    // 新放进去一条数据
    public static void putIt(String name, String date) {
	// 正式的逻辑部分
        List<String> dates = userSign.getOrDefault(name, new ArrayList<>());
        if (!dates.contains(date)) {
            dates.add(date);
        }
        userSign.put(name, dates);
    }

复制代码

能够看到比较麻烦,并且要不是有Map的getOrDefault()方法,代码还得多几行,由于还要分为已存在和未存在来搞..

Guava中提供了一种数据结构,来保存这种一个key对应多个value的状况,就是MultiMap.

虽然他的名字带有map,可是看源码能够发现,他的类生命没有继承Map接口.

要使用MultiMap来实现上面的方法,只须要这样子:

ArrayListMultimap<String, String> userSign = ArrayListMultimap.create();
        userSign.put("huyan", "2019-05-01");
        userSign.put("huyan", "2019-05-02");

        List<String> huyanSign = userSign.get("huyan");

复制代码

是的,直接声明放入就行了,要消费的时候,使用get方法能够得到一个Arratlist,遍历操做便可.

下载了Guava的源码就能够发现,其实他里面就是用Map<String,List>来实现的,这是定义的地方:

2019-05-01-17-15-23

能够看到定义了一个:Map<K,Collection<V>.定义为Collection是为了实现其余的集中数据结构.

好比:

  • HashMultimap的值是放在set中
  • LinkedListMultimap的值放在LinkedList中.

等等.

Multiset -> 一个名叫set的计数器

老实说这个挺好用的,可是为啥要叫set呢...你们对set的印象都是不能够放入重复元素,可是Multiset的做用就是对重复元素计数..

使用方式以下:

Multiset<String> s = HashMultiset.create();
        s.add("pf");
        s.add("pf");
        s.add("pf");
        s.add("hh");
	// i =3
        int i = s.count("pf");

复制代码

这个和我前几天写的计数器的做用是同样的,计数器传送门.

内部实现使用HashMap来保存key->count的一个映射关系.

BiMap -> value也不能够重复的双向Map

这个类是真的实现了JDK的Map接口的,使用它必须保证key和value都没有重复值.由于他支持根据value获取key,即将HashMap的key和value进行调换.

BiMap<String, String> m = HashBiMap.create();

        m.put("pf", "111");

        String value = m.get("pf");
        String key = m.inverse().get("111");
复制代码

这个类适合用在key和value都惟一,且常常会出现根据value来获取key的状况.

Table -> Map<String,Map<String,Object>>的解决方案

碰到多个索引一个结果的时候,Map<String,Map<String,Object>>这种实现方式固然是能够的,可是实在是太让人难以看懂和编码了.

Guava提供了一种名叫Table的数据结构,能够优雅的实现.

使用以下:

Table<Integer, Integer, String> tt = HashBasedTable.create();
        tt.put(1, 2, "huyan");

        String name = tt.get(1, 2);
        Map<Integer, String> row = tt.row(1);
        Map<Integer, String> colum = tt.column(1);
        Set<Table.Cell<Integer, Integer, String>> ha = tt.cellSet();

复制代码

初始化方式和上面的几种结构没有什么区别,都是经过静态工厂方法进行初始化,get和put方法根据两个索引来存放和惟一索引一条数据.

此外,还能够拿到某一行或者某一列的Map结构,能够拿到全部单元格的一个set.极大的方便了处理相似于表格的数据.

固然,看一下源码就会发现,其实Table底层也是使用两个map的嵌套实现的,可是Java语言嘛,讲究的就是一个封装,虽然咱们能够本身实现,可是咱们应该作的是去学习一下好的实现方法,看懂,理解而且可以在其余场景应用相似的思想,而不是每次都要本身写两个map.毕竟现成好用的轮子,在适用的场景下仍是应该多多使用加深理解.

ComparisonChain -> 功能强大且好看的多字段比较方法

在面对多个字段排序比较的场景,通常咱们的代码都会比较难看,好比对下面这个类:

private static class Student {
        int id;
        String name;
        int age;
    }

复制代码

咱们如今是没有办法对其进行比较,或者进行排序的,由于没有定义对于他的比较策略,假设咱们的策略是:

首先比较id,id相等比较name,name相等比较age,这是一种很常见的多字段比较策略.那么咱们给Student类加上Comparable的实现.

// 为了简洁起见,没有写外部类代码,只贴了重写的comparTo方法.
        @Override
        public int compareTo(Object o) {
            Student s = (Student) o;
            int idResult = s.id - this.id;
            int nameResult = s.name.compareTo(this.name);
            int ageResult = s.age - this.age;

            return idResult != 0 ? idResult : nameResult != 0 ? nameResult : ageResult;
        }

复制代码

最后那一串?:?:是否是看的眼睛疼,固然你能够选择三重if-else,我以为也没好到哪里去.

可是可使用ComparisonChain,这名字一看就是比较链,很是适合咱们的场景.改写以下:

@Override
        public int compareTo(Object o) {
            Student s = (Student) o;
            return ComparisonChain.start().compare(s.id, this.id).compare(s.name, this.name).compare(s.age, this.age).
                    result();
        }
复制代码

这个代码可读性的提高可谓是十分大了,语义十分清晰,能够很清楚的看懂开始,先比较,再比较,返回结果这样的一个过程.

Ordering -> 多种比较器的组合

上面的ComparisonChain解决了在实现Comparable时候多个字段排序的状况,那么JDK中有不少的方法须要提供外部的比较器,这时候咱们但愿以多个比较器进行排序呢?

Ordering提供了使用多个比较器的方法,它自身实现了Comparator接口,能够集成多个比较器.

Ordering<Student> studentOrdering = Ordering.compound(Arrays.asList((o1, o2) -> {
            return ComparisonChain.start().result();
        }, (o1, o2) -> 0, (o1, o2) -> 0));
Collections.sort(students, studentOrdering);
复制代码

上面的代码中,使用Ordering集成了多个比较器,以后将其自身传入Collectionssort方法,使用它对list进行排序.





ChangeLog

2019-05-01 完成

以上皆为我的所思所得,若有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文连接。

联系邮箱:huyanshi2580@gmail.com

更多学习笔记见我的博客------>呼延十

相关文章
相关标签/搜索