Guava官方文档中文版(一)-介绍基本工具

Guava是一个包含多种Java核心工具库,如集合、缓存、并发库和注解等。它提供了可读的指南,介绍如何使用和避免null、实现预处理检查、使用Optional、排序和集合工具等。Guava还包含了并发工具如ListenableFuture和服务接口,以及简化异常处理和I/O操作的方法。此外,它支持图数据结构和本地缓存,还有数学、反射和事件总线等功能。
摘要由CSDN通过智能技术生成

用户指南

Guava项目包含几个在我们基于java项目中依赖的Google的核心类库:集合(collections),缓存(caching),原始类型支持,并发库,常用注解,字符串处理,I/O等等。这些工具每天都正在被google人在生产服务中使用。
但是全面研究javadoc并不总是学习如何最好地利用库的有效方法。这里,我们尝试提供的可读的,惬意的说明,一些最受欢迎和功能强大的功能的Guava。
这个viki还在进行中,部分内容可能仍在建设中

  • 基本工具:使使用Java更加愉快。

    • 使用和防止null:null可能是模棱两可的,可能会导致令人困惑的错误,而且有时是非常不愉快的。许多Guava实用工具会快速拒绝和失败null,而不是盲目地接受他们。
    • 前置条件:测试方法的前置条件更容易。
    • 常用的类方法:实现Object方法更简单,像hashCode()toString()
    • 排序:Guava功能强大的流式Comparator类。
    • 异常:简化异常和错误的传播和检查。
  • 集合:Guava对JDK集合生态系统的扩展。这些是Guava最受欢迎最成熟的部分。

    • 不变集合:用于防御性编程,常量集合和提升效率。
    • 新集合类型:用于JDK不能解决的集合:multisets,multimaps,tables,双向映射等等。
    • 功能强大的集合工具:在java.util.Collections没有提供的常用操作。
    • 扩展工具集:要写一个Collection装饰?实现Iterator?我们能够更容易实现。
  • 各种图:图结构数据建模类库,即,实体和他们之间的关系。关键特性包含:

    • :一个图,其边是匿名的实体,没有他们自己的身份和信息。
    • 值图:一个图,其边界有相关联的非唯一的值。
    • 网络:一个图,其边界是唯一对象。
    • 支持可变和不可变的,有向和无向的图,以及其他的一些属性。
  • 缓存:本地缓存,处理得当,支持各种各样的过期行为。

  • 函数习惯:如果谨慎使用,Guava的函数习惯可以明显地简化代码。

  • 并发(Concurrency):功能强大,简单抽象能够更容易写正确的并发代码。

    • ListenableFeture:当完成时带有回调的Feture。
    • Service:启动和关闭的东西,为你照顾困难的状态逻辑。
  • 字符串:一个非常有用的字符串实用程序。分隔,拼接,填充等等。

  • 基本数据类型:JDK没有提供的基本数据类型的操作,intchar。包含一些类型无符号变量。

  • 范围:Guava强大的API,用于处理Comparable上的范围,包含连续类型和离散类型。

  • I/O:简化I/O操作,特别是在整个I/O流和文件上,适用于Java 5和6。

  • 哈希:比Object.hashCode()所提供的更复杂的哈希的工具,包括Bloom过滤器。

  • 事件总线:在组件之间的发布订阅模式通信,无需组件显性地注册另外一个。

  • 数学:JDK没有提供的,经过优化,彻底测试过的数学实用工具。

  • 反射:Guava的Java的反射功能的工具。

  • 提示:使用Guava使你的应用程序按照你希望的方式工作。

注意:要讨论此viki的内容,请使用Guava讨论邮件列表。

基本工具

使用和防止Null

“Null sucks” - Doug Lea
“I call it my billion-dollar mistake” - Sir C. A. R. Hoare ,关于他发明的空引用

null的疏忽使用可能导致各种各样令人错愕的bug。通过研究Google代码库,我们发现像95%的集合中不应该有任何null值,并且应该快速失败,而不是默默接收null值,这对开发人员是有帮助的。

此外,null是令人讨厌的模糊。null返回值的含义并不明显。–例如,Map.get(key)可能返回null,或者因为值在map中为null,或者值没有在map中。Null可以代表失败,可以代表成功,可以代表任何东西。使用非null的东西可以使你的含义更清晰。

也就是说,有时候使用null也是正确的。对内存和速度而言,null是廉价的,在对象数组中它是不可避免的。但是在应用程序代码中,与类库不同的是,它是混淆,困难和奇怪的bug以及令人不快的歧义的主要来源–例如,当Map.get返回null,它可以表示值是不存在的,或者值存在且为null,最重要的一点,null没有指明null值的含义。

出于这些原因,许多Guava工具被设计成在null出现时快速失败,而不是允许null被使用,只要有null值友好的解决方案可用。此外,Guava提供多个工具,既可以在必要时使使用null更容易,也可以帮助你避免使用null

具体案例

如果你在尝试在Set中或者在map中使用null值,或者作为Map中键–不要这么做。如果在查找操作期间显示地使用特殊情况下的null它会更清晰(不那么令人惊讶)。

如果你想要使用null作为map的值,不考虑此项;保留一个单独的Set的非空键(或空键);它是非常容易混淆Map包含一个值为null的key的条目的情况和Map没有此key的条目。最好将这些键分开,并考虑当这些值与为null的key关联时,对于你的应用程序的意义。

如果你正在List中使用null,如果list是稀疏的,你是否更愿意使用Map<Integer,E>?这可能实际上更有效,可能更精确地匹配你应用程序的需求。

考虑是否有一个天然的“空对象”可以使用。并不总是这样。有时候是的。例如,如果它是一个枚举,添加一个常量来表示你期望的null值在这里表示的任何意义。例如,java.math.RoundingModeUNNECESSARY来表示不取舍,而且如果取舍是必须的,则会抛出一个异常。

如果你确实需要null值,并且使用了敌视null值的集合实现,使用不同的实现。例如,使用Collections.unmodifiableList(Lists.newArrayList())代替ImmutableList

Optional

许多情况,程序员使用null表示某种形式的缺席:也许有值的地方,一个值也没有,或者找不到。例如,当key找不到值时,Map.get返回null

Optional<T>是使用一个非空的值替换一个可空的T引用,Optional可能包含一个非空的引用(此情况称为引用是“存在的”),或者什么也没包含(此情况称为“不存在”)。它从来不称为“包含null”。

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

Optional并不打算作为其他程序环境已有的“option”或者“maybe”结构的类似物,尽管它有一些类似之处。
这里我们列出一些最常用的Optional操作。

创建一个Optional

这些都是在Optional上的静态方法:

方法描述
Optional.of(T)创建一个包含给定非空的值的Optional,否则当为null时快速失败
Optional.absent()返回一个不存在的某种类型的Optional
Optional.fromNullable(T)将一个可能为空的引用放入到Optional,将非空视为存在(present),将空视为不存在(absent)
查询方法

这些都是在特别的Optional<T>值上的非静态的方法。

方法描述
boolean isPresent()如果此Optional包含一个非空的实例则返回true
T get()返回包含的T实例,其必须存在;否则,抛出一个IllegalStateException
T or(T)返回在Optional中已存在的值,或者如果没有值,返回默认值
T orNull()返回在Optional中已存在的值,或者如果没有值,则返回为null,fromNullable的相反操作
Set<T> asSet()返回不可变的单例Set,其包含Optional的实例,如果没有值,则返回一个空的不可变Set

除了这些,Optional提供了几种更加方便的工具方法;请查阅Javadoc了解更多详情。

有什么意义?

除了为给定null一个名称增加可读性之外,Optional最大的优势是它的易于操作的。如果你想要编译程序,它迫使你主动考虑不存在的情况,因为你必须主动打开Optional并处理该情况。Null很容易让人不安地忘记一些事情,尽管FindBugs有帮助,但是我们认为它没有很好的解决这个问题。

当你返回的值可能存在或者不存在时这就比较有意义的。对于other.method(a, b)方法,当你实现other.method方法是,与忘记a可能是null相比,你(或者其他人)可能更容易忘记可能返回空值。返回Optional使调用者不可能忘记此情况,因为他们必须打开他们以编译他们。

方便的方法

当你想要null值被一些默认值替换,使用MoreObjects.firstNonNull(T, T)。正如方法名所示,如果输入都是空值,它将使用NullPointerException快速失败。如果你正在使用Optional,有更好的替代方法–即,first.or(second)

Strings中提供了一些处理可能为null字符串的方法。确切来说,我们提供了恰当命名的:

我们想要强调的是这些方法主要是用于与使人不快的API进行接口,那些接口将empty字符串和null字符串等价。每次你写合并null字符串和empty字符串的代码,Guava都会感到很难过(如果null字符串和empty字符串实际表示不同的含义,那最好了,当时如果将他们视为相同的东西是一个令人不安的常见的代码味道)。

预先处理(precondition)

Guava在Preconditaions类中提供了多个预先检查的工具。我们强烈建议静态引入这些类。
每个方法有三个变体:

  1. 没有额外的参数。抛出的任何异常都不会带有错误信息。
  2. 一个额外的Object参数。抛出的任何异常都会带有object.toString()错误信息。
  3. 一个额外的String参数,带有任意数量的额外Object参数。此行为有点像printf,都是为了GWT的兼容性和效率,它只支持%s标志符。
    • 注意:checkNotNull,checkArgument,checkState有大量的重载,将原生类型和Object参数组合,而不是可变数组–这就允许类似于以上调用在绝大数情况下防止原生类型装箱变量数组分配。

三种变体的示例:

checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
checkArgument(i < j, "Expected i < j, but %s >= %s", i, j);
签名(不包含额外参数)描述失败时抛出的异常
checkArgument(boolean)检查boolean是否为true,用于验证方法的参数。IllegalArgumentException
checkNotNull(T)检查值是否为null,否则直接返回此值,所以你可以内联使用checkNotNull(value)NullPointerException
checkState(boolean)检查对象的一些状态,不取决于方法参数。例如,Iterator可以使用它来检查在removie调用之前,next是否已经被调用IllegalStateException
checkElementIndex(int index, int size)检查index是否是指定大小的的list,string或者array的有效的元素(element)索引。元素索引可能可能包含0到不包含size大小。你不能直接传入到list,string,array;你只能传入它的长度。返回indexIndexOutOfBoundsException
checkPositionIndex(int index, int size)检查index是否是指定大小的的list,string或者array的有效的位置(element)索引,位置索引可能包含0到包含size大小。你不能直接传入到list,string,array。你只能传入它的长度。返回indexIndexOutOfBoundsException
checkPositionIndexes(int start, int end, int size)检查startend是否在范围[0-size] (并且end至少于start一样大。自带错误信息。)IndexOutOfBoundsException

相比于Apache Common的类似的工具,我们首选我们自己的前置条件检查,有几点原因。
Briefly:

  • 静态引入之后,Guava方法是清晰的,不模糊的。checkNotNull明确正在做什么,以及什么异常被抛出。
  • checkNotNull在验证之后返回它的参数,允许在构造器中使用一行程序this.field = checkNotNull(field);
  • 简单,可变参数的printf风格的异常信息。(这个优点也就是为什么我们推荐继续使用checkNotNull而不是Objects.requireNonNull)。

我们建议你将多个前置条件分为不同的行,可以帮助当你调试时,断定哪个前置条件失败。此外,你应该提供有帮助的错误信息,当每一个检查是在它自己的行时,这就更容易了。

条件失败

条件失败或者运行时检查是当且仅当一个boolean条件成立时抛出异常的任何代码。当然,这样的代码在设计较好的软件中是常见的。本章节提供这种常见类型检查的概述。

条件失败的种类

一种可以非常容易处理条件失败的相同的方式:if(!condition) throw new RuntimeException();。但是如果你花一点时间考虑正在执行的检查的性质,并以合适的方式处理它,你的代码将更容易被理解和错误更容易被诊断。

下面是主要的几种运行时检查。

预先处理检查确保公共方法的调用者已经遵守了方法规范的要求。例如,sqrt功能可能只接收非负数参数。

一个常规的assertion是一个检查,只有在类本身(包含检查)以某种方式出错时才会失败(在某种情况下,还能扩展到包)。他们可以采用多种形式,包括后置 条件,类不变量和内部前置条件(针对非公共方法)。

当你对你所使用的API满足它的规范(实际或隐含的)缺乏自信时进行验证检查。最容易理解的是这种类型的检查“在任何方面都像断言,我们不想禁用他们”。

测试断言只有在测试代码中发现,并确保测试下的代码已经遵守他自己的规范要求。注意,这种“断言”与生产代码中的真正断言几乎没有任何共同之处。

一个不可能的条件的检查是不可能失败的检查,除非后来修改了周围的代码,或者严重违反了我们关于平台行为的最深层次假设。这些应该是不必要的,但通常是被强制的,因为编译器不能识别语句是不可达的,或者因为我们知道一些关于控制流的信息,而编译器无法推断。

最后,例外结果意味着方法不能提供所期望的结果,既不是由于它自己的错误,也不是任何其他任何代码的错误。这就类似于前提条件检查,除了在这种情况下,不期望调用者知道更多信息。它类似于验证检查,但是依赖的失败并不意外。例如,当已经到达文件末尾时,尝试从文件读取一行,这不是任何人的错;这只是一个例外的结果。根据在Effective Java,Second Edition Item 58,page 244的建议,使用一个检查或者非检查异常。

总结
检查类型抛出方法在说…通常表明…
前置条件[Precondition]“你搞砸了(针对调用者).”IllegalArgumentException,IllegalStateException
断言[Assertion]“我搞砸了.”assert,AssertionError
验证[Verification]“我依赖的人搞砸了.”VerifyException
测试断言[Test assertion]“我测试的代码搞砸了.”assertThatassertEquals ,AssertionError
不可能的条件[Impossible condition]“什么?这个世界太乱了.”AssertionError
异常结果[Exceptional result]“确切地说,没有人搞砸.”其他检查或者未检查异常
重要的不是条件本身,而是上下文

注意每一个相同的条件,例如负数员工ID,在一部分系统(用户系统或者系统对系统接口)中可能是一个“异常的结果”,而在该点以下的所有公共API边界上它是一个“前置条件”。并且如果处于某种原因对非公共方法参数执行相同的检查,它将被正确地认为是“断言”,因为我们应该阻止rogue值走那么远。上下文才是最重要的,并使用不同种类的条件失败传到上下文。

排序(Ordering)

示例

assertTrue(byLengthOrdering.reverse().isOrdered(list));

概览

Ordering是Guava的流式Comparator类,其可以用于构建负责的比较器并将他们应用到对象集合。

在他的核心,Ordering实例只不过是一个特殊的Comparator实例。Ordering只是接受依赖于Comparator的方法(例如,Collections.max)并使他们作为实例方法可用。为了获取更强大的功能,Ordering类提供了调整和增强已有比较器的链式方法。

创建

常用的排序通过静态方法提供:

方法描述
natural()在排序类型上使用natural ordering
usingToString()通过字符串表示的字典排序比较对象,按照toString返回的。

将一个预存在的Comparator放入到Ordering就像使用Ordering.from(Compatator)那么简单。

但是创建一个自定义的Ordering更常用的方式是完全跳过Comparator,直接继承Ordering抽象类。

Ordering<String> byLengthOrdering = new Ordering<String>() {
  public int compare(String left, String right) {
    return Ints.compare(left.length(), right.length());
  }
};

链式方式

给定的Ordering可以进行包装来获取衍生的排序。一些最常用的变体包括:

方法描述
reverse()返回反向的排序
nullsFirst()返回空在非空元素前面的Ordering,其他行为与原有Ordering一样。也可以查看nullsLast()
compound(Comparator)返回使用了“打破关系”的特殊Comparator
compound(Comparator)返回使用了“打破关系”的特殊ComparatorOrdering
lexicographical()返回一个Ordering,其可以按照可迭代对象的元素按照字典顺序排序
onResultOf(Function)返回一个根据应用到他们的函数的值进行排序的Ordering,然后使用原始的Ordering比较结果

例如,假设你想要一个类的比较器:

class Foo {
  @Nullable String sortedBy;
  int notSortedBy;
}

比较器可以处理sortedBy的空值。下面是一个建立在链式方法之上的解决方案:

Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(new Function<Foo, String>() {
  public String apply(Foo foo) {
    return foo.sortedBy;
  }
});

当读到Ordering调用的链式,是从右到左的“反向”工作。以上示例通过查找他们的sortedBy字段值排序Foo实例,优先移动任何空sortedBy值到顶端,然后根据自然字符串顺序排序其余的值。发生向后排序因为每一个链式调用是前面的Ordering“封装”到新的一个中。

(“向后”规则的异常:对于调用compound的链,从左向右。为防止混淆,避免将compound调用与其他链式调用混合。)

比一些调用还要长的一些链可能比较难以理解。我们推荐像上面示例中一样,限制三次调用的链。即使那样,你可能希望通过分割中间对象来简化代码,就像Function实例。

Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(sortKeyFunction);

应用程序

Guava提供多个通过使用ordering操纵或者检查值和集合的方法。这里我们列出一些最流行的:

方法描述还可以查看
greatestOf(Iterable iterable, int k)按照从最大到最小的顺序,按照排序,返回指定迭代中的k个最大元素。不一定稳定leastOf
isOrdered(Iterable)测试指定的Iterable是否是根据此排序为递减的排序。isStrictlyOrdered
sortedCopy(Iterable)返回一个指定元素的排序副本作为ListimmutableSortedCopy
min(E, E)根据排序返回两个参数的最小值,如果两个值相等,返回第一个参数max(E, E)
min(E, E, E, E...)根据排序返回参数中的最小值,如果有多个最小值,返回第一个max(E, E, E, E...)
min(Iterable)返回指定Iterable的最小值,如果Iterable为空,抛出NoSuchElementExceptionmax(Iterable),min(Iterator),max(Iterator)

Object常用方法

equals

当你的对象字段可以为null时,实现Object.equals可能会很痛苦,因为你必须单独的检查是否为null。使用Objects.equal允许你以null敏感的方式执行equals检查,没有NullPointerException风险。

Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true

注意:在JDK7中最新引进的Objects类提供了等价的Objects.equals方法。

hashCode

哈希Object的所有字段更简单。Guava的Objects.hashCode(Object...)为指定字段序列创建一个合理的,顺序敏感的hash。使用Objects.hashCode(field1, field2, ..., fieldn)代替手动构建hash。
注意:在JDK7中最新引进的Objects类提供了等价的Objects.hash(Object...)

toString

一个好的toString方法在调试中是非常有价值的,但是编写起来很痛苦。使用MoreObjects.toStringHelper()可以更容易创建一个有用的toString。一些简单的示例包括:

   // Returns "ClassName{x=1}"
   MoreObjects.toStringHelper(this)
       .add("x", 1)
       .toString();

   // Returns "MyObject{x=1}"
   MoreObjects.toStringHelper("MyObject")
       .add("x", 1)
       .toString();

compare/compareTo

实现Comparator,或者直接实现Comparable接口,可能比较痛苦,考虑以下情况:

class Person implements Comparable<Person> {
  private String lastName;
  private String firstName;
  private int zipCode;

  public int compareTo(Person 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);
  }
}

这个代码非常容易混乱,难以扫描bug并且冗长得令人讨厌。我们可以做的更好。
处于这样的目的,Guava提供ComparisonChain
ComparisonChain提供一个”懒式“比较:它只直到它发现一个非0结果时才会执行比较,之后它忽略进一步的输入。

   public int compareTo(Foo that) {
     return ComparisonChain.start()
         .compare(this.aString, that.aString)
         .compare(this.anInt, that.anInt)
         .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
         .result();
   }

这个流式的语法更具有可读性,不容易出现意外的错别字,而且足够聪明,不会做不必要的工作。其他的比较工具可以在Guava ”流式比较器“类Ordering找到,解释请查看这里

Throwables

Guava的Throwables工具通常可以简化异常处理。

传播

有时,当你捕获一个异常,你想要将他抛回到下一个try/catch块。RuntimeException或者Error实例通常是这种情况,他们不需要try/catch块,但是当你不希望他们被try/catch捕获时,已经通过try/catch块捕获到。

Guava提供了几个工具来简化传递异常。例如:

try {
  someMethodThatCouldThrowAnything();
} catch (IKnowWhatToDoWithThisException e) {
  handle(e);
} catch (Throwable t) {
  Throwables.throwIfInstanceOf(t, IOException.class);
  Throwables.throwIfInstanceOf(t, SQLException.class);
  Throwables.throwIfUnchecked(t);
  throw new RuntimeException(t);
}

下面是Guava提供的传播方法的快速汇总:

签名说明
void propagateIfPossible(Throwable, Class<X extends Throwable>) throws X只有当异常是RuntimeExceptionError和一个X原样抛出throwable
void throwIfInstanceOf(Throwable, Class<X extends Exception>) throws X只有当异常是X实例,原样传递throwable
void throwIfUnchecked(Throwable)只有异常是RuntimeException或者Error,原样抛出异常

注意:我们在v20.0废弃了Throwables.propagate(Throwable)阅读关于为什么

因果链

Guava使它稍微简化异常的因果链的了解,提供三个有用的方法,他们的签名是自解释的:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值