该篇文章为转载文章, Guava 的功能强大,由于自己使用 Guava 并没有特别频繁,很多工具类也是在阅读本篇文章之后才发现原来开发的过程中可以省去很多的繁琐,例如 Charsets 的使用,例如 HttpHeaders 的使用,例如 Multimap 相关集合类的使用,都是在阅读过本篇文章之后才发现可以在实际开发中省去很多的繁琐的。
只不过本篇文章中还有挺多需要补充的地方,后续有时间我会自己实现代码然后做补充的。
另外,本篇文章在做转载时,已经把 Guava 的版本升级为 23.0 ,所以有一些方法做了修改

Guava介绍

Guava 工程包含了若干被 Google Java 项目广泛依赖的核心库,例如:集合 [ collections ] 、缓存 [ caching ] 、原生类型支持 [ primitives support ] 、并发库 [ concurrency libraries ] 、通用注解 [ commonannotations ] 、字符串处理 [ string processing ] 、 I/O 等等。所有这些工具每天都在被 Google 的工程师应用在产品服务中。
这些高质量的 API 可以使你的 Java 代码更加优雅,更加简洁,让你的工作更加轻松愉悦,下面我们来开启 Java 编程学习之旅。

源码包的简单说明
  • com.google.common.annotations :普通注解类型
  • com.google.common.base :基本工具类库和接口
  • com.google.common.cache :缓存工具包,非常简单易用且功能强大的 JVM 内缓存
  • com.google.common.collect :带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合
  • com.google.common.eventbus :发布订阅风格的事件总线
  • com.google.common.hash : 哈希工具包
  • com.google.common.io I/O 工具包
  • com.google.common.math :原始算术类型和超大数的运算工具包
  • com.google.common.net :网络工具包
  • com.google.common.primitives :八种原始类型和无符号类型的静态工具包
  • com.google.common.reflect :反射工具包
  • com.google.common.util.concurrent :多线程工具包
  • 1 基本工具( Basic utilities

    1.1 使用和避免 null Optional

    null 会引起歧义,会造成让人迷惑的错误,有时也会让人感到不爽。 Guava 中的许多工具遇到 null 时,会拒绝或者马上报错,而不是盲目的接受。
    鉴于此 Google Guava 库中提供了 Optional 接口来使 null 快速失败,即在可能为 null 的对象上做了一层封装,在使用 Optional 静态方法 of 时,如果传入的参数为 null 就抛出 NullPointerException 异常。
    Guava Optional 类就是用来强制提醒程序员,注意对 null 的判断。
    Optional 的另外几个方法

    Optional<T>.of(T)
    Optional 赋值,当 T null 直接抛 NullPointException ,建议这个方法在调用的时候直接传常量,不要传变量

    1.2 前提条件( Preconditions

    使方法的条件检查更简单。
    Guava Preconditions 类中提供了若干前置条件判断的实用方法,我们强烈建议在 Eclipse 中静态导入这些方法。每个方法都有三个变种:

  • 没有额外参数:抛出的异常中没有错误消息;
  • 有一个 Object 对象作为额外参数:抛出的异常使用 Object.toString() 作为错误消息;
  • 有一个 String 对象作为额外参数,并且有一组任意数量的附加 Object 对象:这个变种处理异常消息的方式有点类似 printf ,但考虑 GWT 的兼容性和效率,只支持 %s 指示符。
  • 例如:查看源代码打印帮助

    checkNotNull(T) 检查 value 是否为 null ,该方法直接返回 value ,因此可以内嵌使用 checkNotNull NullPointerException checkState(boolean) 用来检查对象的某些状态。 IllegalStateException checkElementIndex(int index, int size) 检查 index 作为索引值对某个列表、字符串或数组是否有效。 index >= 0 && index < size IndexOutOfBoundsException checkPositionIndex(int index, int size) 检查 index 作为位置值对某个列表、字符串或数组是否有效。 index >= 0 && index <= size IndexOutOfBoundsException checkPositionIndexes(int start, int end, int size) 检查 [start, end] 表示的位置范围对某个列表、字符串或数组是否有效 IndexOutOfBoundsException
    1.3 常见的对象方法( Objects

    简化 Object 方法实现,如 hashCode() equals()

    equals()
    当一个对象中的字段可以为 null 时,实现 Object.equals 方法会很痛苦,因为不得不分别对它们进行 null 检查。使用 Objects.equal 帮助你执行 null 敏感的 equals 判断,从而避免抛出 NullPointerException

    Guava 强大的”流畅风格比较器”,具体到下章会介绍到。

    1.5 Throwable

    简化了异常和错误的传播与检查;
    Guava 类库中的 Throwables 提供了一些异常处理的静态方法,这些方法的从功能上分为两类,一类是帮你抛出异常,另外一类是帮你处理异常。

    方法作用

    2 集合( Collections

    介绍 Guava Jdk 集合类的扩展,包括不可变集合,新集合类型包括 Multisets , Multimaps , Tables 等。强大的集合工具类,提供 java.util.Collections 中没有的集合工具,扩展工具类。让实现和扩展集合类变得更容易,比如创建 Collection 的装饰器,或实现迭代器。集合 API 的使用,可以简化集合的创建和初始化。
    Guava API 提供了有用的新的集合类型,协同已经存在的 Java 集合工作。
    分别是 MultiMap Multiset Table BiMap ClassToInstanceMap

    2.1 Guava 的不可变集合

    不可变对象有很多优点:

  • 当对象被不可信的库调用时,不可变形式是安全的
  • 当不可变对象被多个线程调用时,不存在竞态条件问题
  • 不可变集合不需要考虑变化,因此可以节约时间和空间,所有不可变集合都比可变集合形式有更好的内存利用率(分析和测试细节)
  • 不可变对象因为有固定不变,可以用作常量来安全使用
  • 创建对象的不可拷贝是一项很好的防御性编程技巧, Guava 为所有 JDK 标准集合类型和 Guava 新集合类型都提供了简单易用的不可变版本。

    JDK 也提供了可以将集合变成不可变的方法, Collections.unmodifiableXXX ,但是被认为是不好的。

  • 笨重而且累赘:不能舒适地用在所有想做防御性拷贝的场景
  • 不安全:要保证没人通过原集合的引用进行修改,返回的集合才是事实上不可变的
  • 低效:包装过的集合仍然保有可变集合的开销,比如并发修改的检查、散列表的额外空间,等等
  • 2.2 Guava 集合之 Multiset

    Multiset 看似是一个 Set ,但是实质上它不是一个 Set
    它没有继承 Set 接口,它继承的是 Collection<E> 接口
    你可以向 Multiset 中添加重复的元素, Multiset 会对添加的元素做一个计数
    它本质上是一个 Set 加一个元素计数器
    显然计数不是问题, Multiset 还提供了 add remove 的重载方法,可以在 add remove 的同时指定计数的值。

    常用实现 Multiset 接口的类有:

  • HashMultiset : 元素存放于 HashMap
  • LinkedHashMultiset :元素存放于 LinkedHashMap ,即元素的排列顺序由第一次放入的顺序决定
  • TreeMultiset :元素被排序存放于 TreeMap
  • EnumMultiset : 元素必须是 Enum 类型
  • ImmutableMultiset :不可修改的 Mutiset
  • 看到这里你可能已经发现 Guava Collections 都是以 create of 这样的静态方法来构造对象。这是因为这些集合类大多有多个参数的私有构造方法,由于参数数目很多,程序员使用起来就很不方便。而且以这种方式可以返回原类型的子类型对象。另外,对于创建范型对象来讲,这种方式更加简洁。

    2.3 Guava BiMap :双向 Map

    我们知道 Map 是一种键值对映射,这个映射是键到值的映射
    而BiMap首先也是一种 Map
    他的特别之处在于,既提供键到值的映射,也提供值到键的映射
    所以它是双向 Map

    BiMap 的常用实现有:

  • HashBiMap : key 集合与 value 集合都有 HashMap 实现
  • EnumBiMap : key value 都必须是 Enum 类型
  • ImmutableBiMap : 不可修改的 BiMap
  • 2.4 Guava Multimap :一键多值的 Map

    有时候我们需要这样的数据类型 Map<String,Collection<String>>
    Guava 中的 Multimap 就是为了解决这类问题的。
    Multimap 提供了丰富的实现,所以你可以用它来替代程序里的 Map<K, Collection<V>> ,具体的实现如下:

    实现 Key 实现 Value 实现
    2.5 Guava 集合之 Table

    Guava 库中还提供了一种二维表结构: Table
    使用 Table 可以实现二维矩阵的数据结构,可以是稀疏矩阵。
    通常来说,当你想使用多个键做索引的时候,你可能会用类似 Map<FirstName, Map<LastName, Person>> 的实现
    这种方式很丑陋,使用上也不友好
    Guava 为此提供了新集合类型 Table
    它有两个支持所有类型的键:
    Table 提供多种视图,以便你从各种角度使用它:

    rowMap() :用 Map<R, Map<C, V>> 表现 Table<R, C, V>
    同样的, rowKeySet() 返回 的集合 Set<R>
    row(r) :用 Map<C, V> 返回给定 的所有列
    对这个 map 进行的写操作也将写入 Table 中。

    类似的列访问方法: columnMap() columnKeySet() column(c) 。(基于列的访问会比基于的行访问稍微低效点)

    cellSet() :用元素类型为 Table.Cell<R, C, V> Set 表现 Table<R,C, V> Cell 类似于 Map.Entry ,但它是用行和列两个键区分的。

    2.6 Guava 集合:使用 Iterators 简化 Iterator 操作

    Iterators Guava 中对 Iterator 迭代器操作的帮助类,这个类提供了很多有用的方法来简化 Iterator 的操作。

    2.7 ClassToInstanceMap 可以实现 map key 值是多个类型

    有的时候,你的 map key 并不是一种类型,他们是很多类型
    你想通过映射他们得到这种类型
    Guava 提供了 ClassToInstanceMap 满足了这个目的
    除了继承自 Map 接口, ClassToInstaceMap 提供了方法
    T getInstance(Class<T>) T putInstance(Class<T>, T) ,消除了强制类型转换。

    2.8 Ordering 犀利的比较器

    Ordering Guava 类库提供的一个犀利强大的比较器工具
    Guava Ordering JDK Comparator 相比功能更强
    它非常容易扩展,可以轻松构造复杂的 comparator ,然后用在容器的比较、排序等操作中
    本质上来说, Ordering 实例无非就是一个特殊的 Comparator 实例
    Ordering 只是需要依赖于一个比较器(例如: Collections.max )的方法,并使其可作为实例方法
    另外, Ordering 提供了链式方法调用和加强现有的比较器。

    常见的静态方法:

  • natural() :使用 Comparable 类型的自然顺序,例如:整数从小到大,字符串是按字典顺序
  • usingToString() :使用 toString() 返回的字符串按字典顺序进行排序
  • arbitrary() :返回一个所有对象的任意顺序,即 compare(a, b) == 0 就是 a == b (identity equality) 。本身的排序是没有任何含义,但是在 VM 的生命周期是一个常量
  • 2.9 ComparisonChain 比较

    实现一个比较器[ Comparator ],或者直接实现 Comparable 接口有时也伤不起。考虑一下这种情况:

    class Student implements Comparable<Student> {

    private String lastName;
    private String firstName;
    private int zipCode;

    //jdk
    public int compareTo(Student other) {
    int cmp =lastName.compareTo(other.lastName);
    if (cmp != 0) {
    return cmp;
    }
    cmp = firstName.compareTo(other.firstName);
    if (cmp != 0) {
    return cmp;
    }
    return Integer.compare(zipCode, other.zipCode);
    }
    }

    这种 Fluent 接口风格的可读性更高,发生错误编码的几率更小
    并且能避免做不必要的工作。

    3 缓存( Caches

    Google Guava 框架提供了内存缓存的功能
    可以很方便的缓存对象,设置生命周期
    及缓存对象的弱引用,强应用,软引用等

    3.1 使用 Guava 做内存缓存

    Guava 中有 cache 包,此包提供内存缓存功能
    内存缓存需要考虑很多问题,包括并发问题,缓存失效机制
    内存不够用时缓存释放,缓存的命中率,缓存的移除等等
    当然这些东西 Guava 都考虑到了。
    Guava 的内存缓存非常强大,可以设置各种选项
    而且很轻量,使用方便
    另外还提供了下面一些方法,来方便各种需要:

  • ImmutableMap<K, V> getAllPresent(Iterable<?> keys) 一次获得多个键的缓存值
  • put() putAll() 方法向缓存中添加一个或者多个缓存项
  • invalidate() invalidateAll() 方法从缓存中移除缓存项
  • asMap() 方法获得缓存数据的 ConcurrentMap<K, V> 快照
  • cleanUp() 清空缓存
  • refresh(Key) 刷新缓存,即重新取缓存数据,更新缓存
  • 3.2 Guava 缓存分析

    Guava 缓存过期时间分为两种,一种是从写入时开始计时,一种是从最后访问时间开始计时
    而且 Guava 缓存的过期时间是设置到整个一组缓存上的
    这和 EHCache Redis Memcached 等不同
    这些缓存系统设置都将缓存时间设置到了单个缓存上

    Guava 缓存设计成了一组对象一个缓存实例
    这样做的好处是一组对象设置一组缓存策略,你可以根据不同的业务来设置不同的缓存策略
    包括弱引用,软引用,过期时间,最大项数等
    另外一点好处是你可以根据不同的组来统计缓存的命中率,这样更有意义一些

    这样做也是有缺点的
    缺点是首先是每个缓存组都需要声明不同的缓存实例,具体到业务程序中可能就是每个业务对象一个缓存了。
    这样就把不同的业务缓存分散到不同的业务系统中了,不太好管理。

    4 函数式风格( Functional idioms

    5 并发( Concurrency

    并发编程是一个难题,但是一个强大而简单的抽象可以显著的简化并发的编写
    出于这样的考虑, Guava 定义了 ListenableFuture 接口并继承了 JDK concurrent 包下的 Future 接口。

    5.1 Guava 并发: ListenableFuture 使用介绍以及示例

    ListenableFuture 顾名思义就是可以监听的Future
    它是对 Java 原生 Future 的扩展增强
    本文介绍 ListenableFuture 的用法和扩展实现
    我们知道 Future 表示一个异步计算任务,当任务完成时可以得到计算结果
    如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态
    这样做,代码复杂,而且效率低下
    使用 ListenableFuture
    Guava 帮我们检测 Future 是否完成了
    如果完成就自动调用回调函数,这样可以减少并发程序的复杂度
    另外 ListenableFuture 还有其他几种内置实现:

  • SettableFuture :不需要实现一个方法来计算返回值,而只需要返回一个固定值来做为返回值,可以通过程序设置此 Future 的返回值或者异常信息
  • CheckedFuture :这是一个继承自 ListenableFuture 接口,他提供了 checkedGet() 方法,此方法在 Future 执行发生异常时,可以抛出指定类型的异常。
  • 5.2 Guava 并发: RateLimiter 限制资源的并发访问线程数

    RateLimiter 类似于 JDK 的信号量 Semphore
    用来限制对资源并发访问的线程数

    /**
    * 通过Monitor的Guard进行条件阻塞
    */
    public classMonitorSample {
    private List<String> list = new ArrayList<String>();
    private static final int MAX_SIZE = 10;
    private Monitor monitor = new Monitor();

    private Monitor.Guard listBelowCapacity = new Monitor.Guard(monitor) {
    @Override
    public boolean isSatisfied() {
    return list.size() < MAX_SIZE;
    }
    };

    public void addToList(String item) throws InterruptedException {
    monitor.enterWhen(listBelowCapacity); //Guard(形如Condition),不满足则阻塞,而且我们并没有在Guard进行任何通知操作
    try {
    list.add(item);
    } finally {
    monitor.leave();
    }
    }
    }

    6 字符串处理( Strings

    6.1 连接器( Joiner

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

    警告: Joiner 实例总是不可变的。用来定义 Joiner 目标语义的配置方法总会返回一个新的 Joiner 实例。这使得 Joiner 实例都是线程安全的,你可以将其定义为 static final 常量。

    6.2 拆分器( Splitter

    JDK 内建的字符串拆分工具有一些古怪的特性
    比如, String.split 悄悄丢弃了尾部的分隔符。例如:

    警告: Splitter 实例总是不可变的。用来定义 Splitter 目标语义的配置方法总会返回一个新的 Splitter 实例。这使得 Splitter 实例都是线程安全的,你可以将其定义为 static final 常量。

    6.3 字符匹配器( CharMatcher

    使用 CharMatcher 的好处更在于它提供了一系列方法
    让你对字符作特定类型的操作:
    修剪[ trim ]、折叠[ collapse ]、移除[ remove ]、保留[ retain ]等等
    CharMatcher 实例首先代表

  • 概念1:怎么才算匹配字符?
  • 概念2:如何处理这些匹配字符?
  • 这样的设计使得 API 复杂度的线性增加可以带来灵活性和功能两方面的增长。

    注: CharMatcher 只处理 char 类型代表的字符;它不能理解 0x10000 0x10FFFF Unicode 增补字符。这些逻辑字符以代理对[ surrogatepairs ]的形式编码进字符串,而 CharMatcher 只能将这种逻辑字符看待成两个独立的字符。

    6.4 字符集( Charsets

    Charsets 针对所有 Java 平台都要保证支持的六种字符集提供了常量引用。尝试使用这些常量,而不是通过名称获取字符集实例。

    6.5 大小写格式( CaseFormat

    CaseFormat 被用来方便地在各种 ASCII 大小写规范间转换字符串。
    比如,编程语言的命名规范
    CaseFormat 支持的格式如下:

    格式范例

    7 原生类型( Primitives

    Java 的原生类型也称原始类型
    也是基本数据类型 byte short int long float double char boolean
    在从 Guava 查找原生类型方法之前,可以先查查 Arrays
    或者对应的基础类型包装类,如Integer
    原生类型不能当作对象或泛型的类型参数使用
    这意味着许多通用方法都不能应用于它们
    Guava 提供了若干通用工具,包括原生类型数组与集合 API 的交互
    原生类型和字节数组的相互转换
    以及对某些原生类型的无符号形式的支持

    原生类型 Guava 工具类(都在 com.google.common.primitives 包)

    Bytes 工具类没有定义任何区分有符号和无符号字节的方法
    而是把它们都放到了 SignedBytes UnsignedBytes 工具类中
    因为字节类型的符号性比起其它类型要略微含糊一些

    int long 的无符号形式方法在 UnsignedInts UnsignedLongs 类中
    但由于这两个类型的大多数用法都是有符号的
    Ints Longs 类按照有符号形式处理方法的输入参数
    此外, Guava int long 的无符号形式提供了包装类
    UnsignedInteger UnsignedLong ,以帮助你使用类型系统
    以极小的性能消耗对有符号和无符号值进行强制转换。
    原生类型数组工具:

    方法签名描述

    符号无关方法存在于 Bytes , Shorts , Ints , Longs , Floats , Doubles , Chars , Booleans 。而 UnsignedInts , UnsignedLongs , SignedBytes , UnsignedBytes 不存在。

    符号相关方法存在于 SignedBytes , UnsignedBytes , Shorts , Ints , Longs , Floats , Doubles , Chars , Booleans , UnsignedInts , UnsignedLongs

    Bytes 不存在。

    通用工具方法:

    |方法签名|描述|
    | int compare(prim a, prim b) |传统的 Comparator.compare 方法,但针对原生类型。 JDK7 的原生类型包装类也提供这样的方法|
    | prim checkedCast(long value) |把给定 long 值转为某一原生类型,若给定值不符合该原生类型,则抛出 IllegalArgumentException |
    | prim saturatedCast(long value) |把给定 long 值转为某一原生类型,若给定值不符合则使用最接近的原生类型值|

    这里的整型包括byte, short, int, long。不包括char, boolean, float, 或double。

    字节转换方法:
    Guava 提供了若干方法,用来把原生类型按大字节序与字节数组相互转换。所有这些方法都是符号无关的,此外 Booleans 没有提供任何下面的方法。

    |方法签名或字段签名|描述|
    | int BYTES |常量:表示该原生类型需要的字节数|
    | prim fromByteArray(byte[] bytes) |使用字节数组的前 Prims.BYTES 个字节,按大字节序返回原生类型值;如果 bytes.length <= Prims.BYTES ,抛出 IAE |
    | prim fromBytes(byte b1, …, byte bk) |接受 Prims.BYTES 个字节参数,按大字节序返回原生类型值|
    | byte[] toByteArray(prim value) |按大字节序返回 value 的字节数组|

    8 区间( Ranges

    8.1 简介

    区间,有时也称为范围
    是特定域中的凸性(非正式说法为连续的或不中断的)部分
    在形式上,凸性表示对 a <= b <= c , range.contains(a) range.contains(c) 意味着 range.contains(b)
    区间可以延伸至无限,例如范围 x > 3 包括任意大于 3 的值,也可以被限制为有限,如 2 <= x < 5
    Guava 用更紧凑的方法表示范围,有数学背景的程序员对此是耳熟能详的:

    上面的 a b 称为端点
    为了提高一致性, Guava 中的 Range 要求上端点不能小于下端点
    上下端点有可能是相等的,但要求区间是闭区间或半开半闭区间(至少有一个端点是包含在区间中的):
    [a..a]:单元素区间
    [a..a); (a..a]:空区间,但它们是有效的
    (a..a):无效区间
    Guava 用类型 Range<C> 表示区间。所有区间实现都是不可变类型。

    8.2 构建区间

    区间实例可以由 Range 类的静态方法获取:

    数学形式区间静态方法
    8.3 区间运算

    Range 的基本运算是它的 contains(C) 方法
    和你期望的一样,它用来区间判断是否包含某个值
    此外, Range 实例也可以当作 Predicate ,并且在函数式编程中使用
    任何 Range 实例也都支持 containsAll(Iterable<? extends C>) 方法

    8.4 查询运算

    Range 类提供了以下方法来 查看区间的端点:

  • hasLowerBound() hasUpperBound() :判断区间是否有特定边界,或是无限的;
  • lowerBoundType() upperBoundType() :返回区间边界类型, CLOSED OPEN ;如果区间没有对应的边界,抛出 IllegalStateException
  • lowerEndpoint() upperEndpoint() :返回区间的端点值;如果区间没有对应的边界,抛出 IllegalStateException
  • isEmpty() :判断是否为空区间。
  • 8.5 关系运算
  • 包含[ enclose ]
    区间之间的最基本关系就是包含[ encloses(Range) ]:如果内区间的边界没有超出外区间的边界,则外区间包含内区间。包含判断的结果完全取决于区间端点的比较
  • 相连[ isConnected ]
    Range.isConnected(Range) 判断区间是否是相连的。具体来说, isConnected 测试是否有区间同时包含于这两个区间,这等同于数学上的定义“两个区间的并集是连续集合的形式”(空区间的特殊情况除外)。
  • 交集[ intersection ]
    Range.intersection(Range) 返回两个区间的交集:既包含于第一个区间,又包含于另一个区间的最大区间。当且仅当两个区间是相连的,它们才有交集。如果两个区间没有交集,该方法将抛出 IllegalArgumentException
  • 跨区间[ span ]
    Range.span(Range) 返回“同时包括两个区间的最小区间”,如果两个区间相连,那就是它们的并集。span是可互换的[ commutative ] 、关联的[ associative ] 、闭合的[ closed ]、运算[ operation ]。
  • 8.6 离散域

    部分(但不是全部)可比较类型是离散的
    即区间的上下边界都是可枚举的。
    Guava 中,用 DiscreteDomain<C> 实现类型 C 的离散形式操作
    一个离散域总是代表某种类型值的全集
    它不能代表类似“素数”、“长度为5的字符串”或“午夜的时间戳”这样的局部域。

    DiscreteDomain 提供的离散域实例包括:

    类型离散域

    一旦获取了 DiscreteDomain 实例,你就可以使用下面的 Range 运算方法:

  • ContiguousSet.create(range, domain) :用 ImmutableSortedSet<C> 形式表示 Range<C> 中符合离散域定义的元素,并增加一些额外操作——译者注:实际返回 ImmutableSortedSet 的子类 ContiguousSet 。(对无限区间不起作用,除非类型 C 本身是有限的,比如 int 就是可枚举的)
  • canonical(domain) :把离散域转为区间的“规范形式”。如果 ContiguousSet.create(a, domain).equals(ContiguousSet.create(b,domain)) 并且 !a.isEmpty() ,则有 a.canonical(domain).equals(b.canonical(domain)) 。(这并不意味着 a.equals(b)
  • 你可以创建自己的离散域,但必须记住 DiscreteDomain 契约的几个重要方面。
    一个离散域总是代表某种类型值的全集;它不能代表类似“素数”或“长度为5的字符串”这样的局部域。所以举例来说,你无法构造一个·DiscreteDomain·以表示精确到秒的 JODA DateTime 日期集合:因为那将无法包含 JODA DateTime 的所有值。
    DiscreteDomain 可能是无限的——比如 BigInteger DiscreteDomain 。这种情况下,你应当用 minValue() maxValue() 的默认实现,它们会抛出 NoSuchElementException 。但 Guava 禁止把无限区间传入 ContiguousSet.create

    9 I/O

    9.1 Guava Files 中的文件操作

    Java 的基本 API 对文件的操作很繁琐,为了向文件中写入一行文本,都需要写十几行的代码。 Guava 对此作了很多改进,提供了很多方便的操作。

    10 散列( Hash

    10.1 概述

    Java 内建的散列码[ hash code ]概念被限制为 32 位,并且没有分离散列算法和它们所作用的数据,因此很难用备选算法进行替换。此外,使用 Java 内建方法实现的散列码通常是劣质的,部分是因为它们最终都依赖于 JDK 类中已有的劣质散列码。
    Object.hashCode 往往很快,但是在预防碰撞上却很弱,也没有对分散性的预期。这使得它们很适合在散列表中运用,因为额外碰撞只会带来轻微的性能损失,同时差劲的分散性也可以容易地通过再散列来纠正( Java 中所有合理的散列表都用了再散列方法)。然而,在简单散列表以外的散列运用中, Object.hashCode 几乎总是达不到要求,因此有了 com.google.common.has h包。

    10.2 散列包的组成

    在这个包的 Java doc 中,我们可以看到很多不同的类,但是文档中没有明显地表明它们是怎样一起配合工作的。

  • HashFunction
    HashFunction 是一个单纯的(引用透明的)、无状态的方法,它把任意的数据块映射到固定数目的位置,并且保证相同的输入一定产生相同的输出,不同的输入尽可能产生不同的输出。
  • Hasher
    HashFunction 的实例可以提供有状态的 Hasher Hasher 提供了流畅的语法把数据添加到散列运算,然后获取散列值。 Hasher 可以接受所有原生类型、字节数组、字节数组的片段、字符序列、特定字符集的字符序列等等,或者任何给定了 Funnel 实现的对象。
    Hasher 实现了 PrimitiveSink 接口,这个接口为接受原生类型流的对象定义了 fluent 风格的 API
  • Funnel
    Funnel 描述了如何把一个具体的对象类型分解为原生字段值,从而写入 PrimitiveSink
    注: putString("abc",Charsets.UTF_8).putString("def", Charsets.UTF_8) 完全等同于 putString("ab", Charsets.UTF_8).putString("cdef",Charsets.UTF_8) ,因为它们提供了相同的字节序列。这可能带来预料之外的散列冲突。增加某种形式的分隔符有助于消除散列冲突。
  • HashCode
    一旦 Hasher 被赋予了所有输入,就可以通过 hash() 方法获取 HashCode 实例(多次调用 hash() 方法的结果是不确定的)。 HashCode 可以通过 asInt() asLong() asBytes() 方法来做相等性检测,此外, writeBytesTo(array, offset, maxLength) 把散列值的前 maxLength 字节写入字节数组。
  • 10.3 布鲁姆过滤器[ BloomFilter ]

    布鲁姆过滤器是哈希运算的一项优雅运用
    它可以简单地基于 Object.hashCode() 实现
    简而言之,布鲁姆过滤器是一种概率数据结构
    它允许你检测某个对象是一定不在过滤器中
    还是可能已经添加到过滤器了
    Guava 散列包有一个内建的布鲁姆过滤器实现
    你只要提供 Funnel 就可以使用它
    你可以使用 create(Funnel funnel, int expectedInsertions, doublefalsePositiveProbability) 方法获取 BloomFilter<T>
    缺省误检率[ falsePositiveProbability ]为 3%
    BloomFilter<T> 提供了 booleanmightContain(T) void put(T)
    它们的含义都不言自明了。

    10.4 Hashing

    Hashing 类提供了若干散列函数
    以及运算 HashCode 对象的工具方法
    已提供的散列函数

  • md5()
  • murmur3_128()
  • murmur3_32()
  • sha1()
  • sha256()
  • sha512()
  • goodFastHash(int bits)
  • HashCode运算

    方法描述

    11 事件总线( EventBus

    传统上, Java 的进程内事件分发都是通过发布者和订阅者之间的显式注册实现的
    设计 EventBus 就是为了取代这种显示注册方式
    使组件间有了更好的解耦
    EventBus 不是通用型的发布-订阅实现,不适用于进程间通信。

    12 数学运算( Math

    13 反射( Reflection

    13.1 Guava 反射 TypeToken 解决泛型运行时类型擦除的问题

    介绍 Guava 中的 TypeToken 类解决 Java 运行时泛型类型擦除问题。
    TypeToken 的方法列表如下:

    方法描述
    13.2 Gava 反射之 Invokable 使用

    Guava Invokable 是对 java.lang.reflect.Method java.lang.reflect.Constructor 的流式包装。它简化了常见的反射代码的使用。
    一些使用例子:

    方法是否是 public 的?
    JDK :
    Modifier.isPublic(method.getModifiers());
    Invokable :
    invokable.isPublic();

    13.3 Guava 反射: Reflection.newProxy 方法简化动态代理

    原理上 Guava 的动态代理也是使用 JDK 的动态代理,这是做了封装,更加简便
    另外一点是能够很好的检查需要代理的对象必须拥有接口。
    使用 Class 类的 isInterface() 来做检查。

    14 注解( Annotations

    com.google.common.annotations 包下注解类的含义和用法:

    14.1 Beta
    /**
    * 表明一个类型可能会与 Google WebToolkit 一起使用.
    * 如果一个方法使用这个注释,说明这个方法的返回值是GWT 兼容的
    *
    */
    @Retention(RetentionPolicy.CLASS)
    @Target({ElementType.TYPE, ElementType.METHOD })
    @Documented
    @GwtCompatible
    public@interface GwtCompatible {

    /**
    * 说明一个类型或者方法的返回值是否支持GWT 序列化
    *
    */
    boolean serializable() default false;

    /**
    * 说明一个类型是否在 GWT 被模拟.
    * 被模拟的源(父源)和JVM的实现不一样
    *
    */
    boolean emulated() default false;
    }
    /**
    * 说明一个方法可能无法与GWT一起使用
    * 他只能用于被@GwtCompatible标志的类的字段,方法和内部类
    *
    */
    @Retention(RetentionPolicy.CLASS)
    @Target({
    ElementType.TYPE, ElementType.METHOD,
    ElementType.CONSTRUCTOR, ElementType.FIELD })
    @Documented
    @GwtCompatible
    public@interface GwtIncompatible {
    /**
    * 用于表示不兼容 GWT 的原因
    *
    */
    String value();
    }

    15 网络编程( Net

    Guava 中的 net 包目前提供的功能较少
    而且大多类都标注了 @Beta 的注解
    Guava 中标记 Beta 注解表示这个类还不稳定
    有可能在以后的版本中变化,或者去掉
    所以不建议大量使用,这里也是只做简单的介绍

    先介绍下唯一一个没有 Beta 注解的类 HttpHeaders
    这个类中并没有实质的方法,只是定义了一些 Http 头名称的常量
    通常如果需要我们会自己定义这些常量
    如果你引用了 guava 包,那么就不再建议我们自己定义这些头名称的常量了,直接用它定义的即可

    这里面应该有几乎所有的 Http 头名称,例如: X_FORWARDED_FOR UPGRADE REFERER 等等
    用法也没有必要介绍了,直接引用常量就可以了

    再介绍下一个比较常用的小功能
    有时候我们需要在配置文件中配置 IP+PORT
    这时候需要自己写解析ip,端口的方法
    Guava 为我们提供了解析类,我们看下用法实例:

    HostAndPort 类的静态方法 fromString(String) 可以解析出字符串中定义的 Host 和端口信息。

    另外 Guava 包中还提供了 InetAddresses
    这个类是 InetAddress 的帮助类
    通过这个类可以方便的从字符串中解析出 InetAddress
    但是此类也有 @Beta 的注解,所以要谨慎使用。

    参考资料:

  • http://ifeve.com/google-guava/
  • https://code.google.com/p/guava-libraries/
  • 原文作者:文/debugo
    原文标题: baoq_v5_java