guava初体验

日前在了解java现流行技术时,发现有一些博客都推荐使用或者在学习guava,因此我也去到guava的官方文档中一探究竟,以下是guava wiki链接,也就有了本篇个人学习认识guava的一点点记录。

guava官方文档指出:Guava项目包含我们在基于Java的项目中依赖的几个Google核心库:集合,缓存,基元支持,并发库,通用注释,字符串处理,I / O等。Google员工每天都会在生产服务中使用这些工具。

因为涉及内容较多,本篇只做部分记录,更多详细内容,大伙可以到官方指南学习。

1.使用Optional来避免空指针异常

Guava用Optional表示可能为null的T类型引用。一个Optional实例可能包含非null的引用(称之为引用存在),也可能什么也不包括(称之为引用缺失)。Optional不会包含null值引用。

import com.google.common.base.Optional;
public class test {
    public static void main(String[] args) {
        //引用存在
        Optional possible = Optional.of(5);
        System.out.println(possible.isPresent());//true
        System.out.println(possible.get());//5

        Set integers = possible.asSet();
        System.out.println(integers);//[5]

        //引用缺失
        String first = null;
        Optional optional = Optional.of(first);//java.lang.NullPointerException

        String second = String.valueOf("second");
        String result =  Optional.fromNullable(first).or(second);
        System.out.println(result);//second

        //引用缺失
        Optional absent = Optional.absent();
        System.out.println(absent.isPresent()); //false
        System.out.println(absent.get());//Optional.get() cannot be called on an absent value
    }
}

在jdk1.8中也提供了Optional api,其api与guava有些不同,需要注意。

创建Optional实例(以下都是静态方法):

Optional.of(T)

创建指定引用的Optional实例,若引用为null则快速失败

Optional.absent()

创建引用缺失的Optional实例

Optional.fromNullable(T)

创建指定引用的Optional实例,若引用为null则表示缺失

用Optional实例查询引用(以下都是非静态方法):

boolean isPresent()

如果Optional包含非null的引用(引用存在),返回true

T get()

返回Optional所包含的引用,若引用缺失,则抛出java.lang.IllegalStateException

T or(T)

返回Optional所包含的引用,若引用缺失,返回指定的值

T orNull()

返回Optional所包含的引用,若引用缺失,返回null

Set asSet()

返回Optional所包含引用的单例不可变集,如果引用存在,返回一个只有单一元素的集合,如果引用缺失,返回一个空集合。

2.常见的Objects方法

使用Objects.equal执行null敏感的equals判断,从而避免抛出NullPointerException。

package com.example.demo.guava.sample.objects;

import com.google.common.base.Objects;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {

        System.out.println(Objects.equal("a","a"));//true

        System.out.println(Objects.equal(null,"a"));//false

        System.out.println(Objects.equal("a",null));//false

        System.out.println(Objects.equal(null,null));//true

    }
}

Guava的Objects.hashCode(Object...)会对传入的字段序列计算出合理的、顺序敏感的散列值。可以使用Objects.hashCode(field1, field2, …, fieldn)来代替手动计算散列值。

package com.example.demo.guava.sample.objects;

import com.google.common.base.Objects;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {

        Foo a = new Foo("a",18);
        int hashCode = Objects.hashCode(a.age, a.name);
        System.out.println(hashCode);
    }
}

Guava提供了ComparisonChain。ComparisonChain执行一种懒比较:它执行比较操作直至发现非零的结果,在那之后的比较输入将被忽略。

package com.example.demo.guava.sample.objects;

import com.google.common.base.Objects;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {

        Foo a = new Foo("a", 18);
        Foo b = new Foo("b", 17);
        System.out.println(b.compareTo(a));//-1
    }
}

public class Foo {
    String name;
    Integer age;

    public Foo(String name,Integer age){
        this.name = name;
        this.age = age;
    }

    public int compareTo(Foo that){
        return ComparisonChain.start()
                .compare(this.age,that.age)
                .compare(this.name,that.name)
                .result();
    }
}


3.新集合Multiset

统计一个词在文档中出现了多少次,传统的做法是这样的:

	Map counts = new HashMap();
	for (String word : words) {
	    Integer count = counts.get(word);
	    if (count == null) {
	        counts.put(word, 1);
	    } else {
	        counts.put(word, count + 1);
	    }
	}

使用Multiset可以这么实现:

	List wordList=new ArrayList();
	for (String word : words) {
	     wordList.add(word);
	}
	Multiset wordsMultiset = HashMultiset.create();
	wordsMultiset.addAll(wordList);
	for(String key:wordsMultiset.elementSet()){
	   System.out.println(key+" count:"+wordsMultiset.count(key));
	}

方法

描述

count(E)

给定元素在Multiset中的计数

elementSet()

Multiset中不重复元素的集合,类型为Set

entrySet()

和Map的entrySet类似,返回Set>,其中包含的Entry支持getElement()和getCount()方法

add(E, int)

增加给定元素在Multiset中的计数

remove(E, int)

减少给定元素在Multiset中的计数

setCount(E, int)

设置给定元素在Multiset中的计数,不可以为负数

size()

返回集合元素的总个数(包括重复的元素)

  • Multiset中的元素计数只能是正数。任何元素的计数都不能为负,也不能是0。elementSet()和entrySet()视图中也不会有这样的元素。
  • multiset.size()返回集合的大小,等同于所有元素计数的总和。对于不重复元素的个数,应使用elementSet().size()方法。(因此,add(E)把multiset.size()增加1)
  • multiset.iterator()会迭代重复元素,因此迭代长度等于multiset.size()。
  • Multiset支持直接增加、减少或设置元素的计数。setCount(elem, 0)等同于移除所有elem。
  • 对multiset 中没有的元素,multiset.count(elem)始终返回0。

Multiset的各种实现:

Guava提供了多种Multiset的实现,大致对应JDK中Map的各种实现:

Map

对应的Multiset

是否支持null元素

补充说明

HashMap

HashMultiset

无序

TreeMap

TreeMultiset

是(如果comparator支持的话)

有序(性能低)

LinkedHashMap

LinkedHashMultiset

有序(性能高)

ConcurrentHashMap

ConcurrentHashMultiset

线程安全

ImmutableMap

ImmutableMultiset

不可修改HashMap

4.新键值对Multimap

Guava的 Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。比如实现Map>或Map>这样的结构类型。并且不会有任何键映射到空集合,一个键要么至少到一个值,要么根本就不在Multimap中。很少会直接使用Multimap接口,更多时候你会用ListMultimap或SetMultimap接口,它们分别把键映射到List或Set。

package com.example.demo.guava.sample.multimap;

import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;

import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {
        ListMultimap listMultimap = MultimapBuilder
                .treeKeys()
                .arrayListValues()
                .build();
        listMultimap.put("a",1);
        listMultimap.put("a",2);
        listMultimap.put("b",1);
        System.out.println(listMultimap);   //{a=[1, 2], b=[1]}

        System.out.println(listMultimap.get("c"));//[]

        List value = listMultimap.get("a");
        value.add(3);
        System.out.println(listMultimap);  //{a=[1, 2, 3], b=[1]}

        listMultimap.removeAll("b");
        listMultimap.remove("a",1);
        System.out.println(listMultimap);  //{a=[2, 3]}

        Map> mapView = listMultimap.asMap();
        System.out.println(mapView);  //{a=[2, 3]}

        //同理Set
        SetMultimap setMultimap = MultimapBuilder
                .treeKeys()
                .hashSetValues()
                .build();
    }
}

修改Multimap的方法有:

方法签名

描述

等价于

put(K, V)

添加键到单个值的映射

multimap.get(key).add(value)

putAll(K, Iterable)

依次添加键到多个值的映射

Iterables.addAll(multimap.get(key), values)

remove(K, V)

移除键到值的映射;如果有这样的键值并成功移除,返回true。

multimap.get(key).remove(value)

removeAll(K)

清除键对应的所有值,返回的集合包含所有之前映射到K的值,但修改这个集合就不会影响Multimap了。

multimap.get(key).clear()

replaceValues(K, Iterable)

清除键对应的所有值,并重新把key关联到Iterable中的每个元素。返回的集合包含所有之前映射到K的值。

multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values)

5.新键值对BiMap

传统上,实现键值对的双向映射需要维护两个单独的map,并保持它们间的同步。但这种方式很容易出错,而且对于值已经在map中的情况,会变得非常混乱。例如:

	Map nameToId = Maps.newHashMap();
	Map idToName = Maps.newHashMap();
	 
	nameToId.put("Bob", 42);
	idToName.put(42, "Bob");

BiMap是特殊的Map:

  • 可以用 inverse()反转BiMap的键值映射
  • 保证值是唯一的,因此 values()返回Set而不是普通的Collection

在BiMap中,如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常。如果对特定值,你想要强制替换它的键,请使用 BiMap.forcePut(key, value)

package com.example.demo.guava.sample.bigmap;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {
        BiMap user = HashBiMap.create();
        user.put("a",1);
        System.out.println(user.get("a"));  // 1
        String userById = user.inverse().get(1);
        System.out.println(userById);//a

        user.forcePut("b",1);//强制替换
        String get = user.inverse().get(1);
        System.out.println(get);//b

        user.put("c",1);//java.lang.IllegalArgumentException: value already present: 1
    }
}

6.新类型Table

通常来说,当你想使用多个键做索引的时候,你可能会用类似Map>的实现,这种方式很丑陋,使用上也不友好。Guava为此提供了新集合类型Table,它有两个支持所有类型的键:”行”和”列”。Table提供多种视图,以便你从各种角度使用它:

package com.example.demo.guava.sample.table;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {
        Table weightedGraph = HashBasedTable.create();
        weightedGraph.put("row1", "col1", 1D);
        weightedGraph.put("row2", "col1", 2D);
        weightedGraph.put("row3", "col3", 3D);
        weightedGraph.put("row1", "col4", 4D);
        System.out.println(weightedGraph.row("row1"));//{col4=4.0, col1=1.0}
        System.out.println(weightedGraph.column("col1"));//{row1=1.0, row2=2.0}
        System.out.println(weightedGraph.column("co15"));//{}
        System.out.println(weightedGraph.get("row1", "col4"));//4.0
    }
}

7.ClassToInstanceMap类型

ClassToInstanceMap是一种特殊的Map:它的键是类型,而值是符合键所指类型的对象。

为了扩展Map接口,ClassToInstanceMap额外声明了两个方法:T getInstance(Class) 和T putInstance(Class, T),从而避免强制类型转换,同时保证了类型安全。

package com.example.demo.guava.sample.classtoinstancemap;

import com.example.demo.guava.sample.objects.Foo;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {
        ClassToInstanceMap numberDefaults= MutableClassToInstanceMap.create();
        numberDefaults.putInstance(Integer.class, Integer.valueOf(0));
        Integer instance = numberDefaults.getInstance(Integer.class);
        System.out.println(instance);//0

        ClassToInstanceMap foo = MutableClassToInstanceMap.create();
        Foo a = new Foo("a", 18);
        foo.putInstance(Foo.class,a);
        Foo fooInstance = foo.getInstance(Foo.class);
        System.out.println(fooInstance);//age:18,name:a
    }
}

8.字符串处理

用分隔符把字符串序列连接起来也可能会遇上不必要的麻烦。如果字符串序列中含有null,那连接操作会更难。Fluent风格的Joiner让连接字符串更简单。

package com.example.demo.guava.sample.joiner;

import com.google.common.base.Joiner;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {
        Joiner joiner = Joiner.on("; ").skipNulls();
        String join = joiner.join("yan", null, "lu", "hong");
        System.out.println(join);
    }
}

 字符串分割 Splitter

package com.example.demo.guava.sample.splitter;

import com.google.common.base.Splitter;

/**
 * @author lovemego
 * @desc
 * @date 2019/8/28
 */
public class test {
    public static void main(String[] args) {
        Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split("yan,liu,,hong,  ,lu");
        split.forEach(c->{
            System.out.println(c);
        });
    }
}

 

你可能感兴趣的:(JAVA)