Guava翻译之Small Function

Guava 的一些小功能点

我们已经到了书的最后一章,但是任然感觉还有很多东西没有覆盖到。虽然在这么短的篇幅里我们不可能覆盖到guava的多有功能,但是我们已经尽力了。 这一章我们将介绍一些没有必要单独一章介绍的一些小的工具类,虽然他们不是天天不用到,但是一旦用到,你就会发现真的非常方便. 这一章我们将学习一下的几个知识点:
1. Hashing 类: 通过Hashing类的静态工具方法我们可以获取HashFunction 实例
2. 布隆过滤: Bloom 过滤这样的数据结构,可以明确返回一个数据不存在,但是不能确保一个元素肯定存在.当存在Hash碰撞时有可能误判。
3. Optional类: Optional 类可以让我们选择使用 null 的引用
4. Throwables类: Throwables 类中有静态工具方法可以与Throwable协作

创建合适的Hash函数

Hash函数是确定对象身份标识和确定是否重复的基础,同时它也是合理使用java结合的必备条件。 Hash函数的基本工作方式: 将对象数据映射为数字。 因此我们希望避免将不同的数据生成相同的数字,毋庸置疑,写一个优秀的Hash函数的工作就留给那些专家吧, 但是幸运的是,在Guava的帮助下我们不需要自己去写Hash函数, Hashing类提供了一些静态方法可以创建HashFunction的实例。

校验总和哈希函数

Guava 提供了两种实现了checksum算法的HashFunction,分别是Adler-32 和 CRC-32,创建这两个HashFunction 可以按照如下的方法:

HashFunction adler32 = Hashing.adler32();
HashFunction crc32 = Hashing.crc32();

这里我们可以简单的使用Hashing函数的静态方法去获取想要的实例.

一般哈希函数

下面我们将看一下一般的hash函数,一般的hash函数是不带加密语义,非常适合基于Hash的查找类任务。 第一个要介绍的就是 murmur Hash,这是一个在2008年由Austin发现的。 其他的一般性hash算法叫做GooFastHash。 下面让我们看下怎样创建这些一般性Hash函数:

HashFunction gfh = Hashing.goodFastHash(128);
HashFunction murmur3_32 = Hashing.murmur3_32();
HashFunction murmur3_128 = Hashing.murmur3_128();

上面的例子中我们使用GoodFastHash算法返回一个最小包含128长度bit位,一个字节包含8个bit位,因此调用GoodFastHash至少返回16个字节(128/8).接下来我们创建了两个murmur实例的Hash. 第一个是实现了32-bit的murmur3_32算法,第二个murmur Hash实例实现了128bit murmur3_128算法。

密码哈希函数

虽然完整的讨论密码哈希函数超出了本书的返回,但是简单的说密码哈希函数就是为了保证数据的安全。 一般来说,密码哈希函数有一些如下的属性:
-- 一点小的数据改变,就会导致HashCode发生很大的变化
-- 在理论上,通过hash code 反推出原始数据是什么是不可能的。

下面Guava提供了创建密码哈希函数的变体:

HashFunction sha1 = Hashing.sha1();
HashFunction sha256 = Hashing.sha256();
HashFunction sha512 = Hashing.sha512();

上面的三种Hash算法实现了sha1,sha256,sha512三种加密算法。

Bloom Filter

Bloom 过滤器是一个独特的数据结构,它可以用来确定一个元素是否存在于一个set中, 但是让Bloom过滤更有意思的是,它能准确的判断一个元素不存在,但是不能准确的判断出一个元素存在于set中。这样的特性可以用在比较耗时的数据查找中.

BloomFilter 简述

BloomFilter实际是一个bit的集合,按照以下方式工作:
1. 增加一个元素到filter中。
2. 将这个元素进行多次Hash,将的到的hash值的bit位设置为1。

当判断一个元素是否存在在set中时,按照同样的方法进行多次hash,如果对应的位上有一个不为1,那么就可以说明这个元素在set中不存在,但是即使每个元素位都是为1,也不能表明这个元素就存在,因为可能会发生Hash碰撞。

Funnels and PrimitiveSinks

Funnels 接口接受一个对象,并将对象数据发送给PrimitveSink接口, PrimitiveSink对象接受原始类型的数据,PrimitiveSink接口抽取参加Hash的数据,下面我们看一下具体的例子:

public enum BookFunnel implements Funnel<Book> {
//This is the single enum value
FUNNEL;
public void funnel(Book from, PrimitiveSink into) {
into.putBytes(from.getIsbn().getBytes(Charsets.UTF_8))
.putDouble(from.getPrice());
}
}

上面的例子中我们创建一个Funnel实例接受一个Book的实例。 注意我们这里通过枚举实现Funnel接口, ISBN和Price被放入到PrimitiveSink实例作为Hash函数的入参。

创建 BloomFilter 实例

上面我们学了怎样去创建一个Funnel实例,现在我们可以去创建BloomFilter实例:

BloomFilter<Book> bloomFilter = BloomFilter.create(new
BookFunnel(), 5);

上面这个例子中,我们通过调用BloomFilter的静态create方法传入一个Funnel实例和一个整数,这个整数其实就是使用的Hash函数个数,比如上面的5,我们其实是使用5个hash函数生成5个对应的Hashcode值。将这5个对应的hashcode值对应的bit位修改为1. 接下来我们来看一个具体的例子:

public class BloomFilterExample {
public static void main(String[] args) throws Exception {
File booksPipeDelimited = new
File("src/main/resources/books.data");
List<Book> books = Files.readLines(booksPipeDelimited,
Charsets.UTF_8, new LineProcessor<List<Book>>() {
Splitter splitter = Splitter.on('|');
List<Book> books = Lists.newArrayList();
Book.Builder builder = new Book.Builder();
public boolean processLine(String line) throws
IOException {
List<String> parts =
Lists.newArrayList(splitter.split(line));
builder.author(parts.get(0))
.title(parts.get(1))
.publisher(parts.get(2))
.isbn(parts.get(3))
.price(Double.parseDouble(parts.get(4)));
books.add(builder.build());
return true;
}
@Override
public List<Book> getResult() {
return books;
}
});
BloomFilter<Book> bloomFilter = BloomFilter.create(new
BookFunnel(), 5);
for (Book book : books) {
bloomFilter.put(book);
}
Book newBook = new Book.Builder().title("Mountain
Climbing").build();
Book book1 = books.get(0);
System.out.println("book "+book1.getTitle()+" contained
"+bloomFilter.mightContain(book1));
System.out.println("book "+newBook.getTitle()+" contained
"+bloomFilter.mightContain(newBook));
}

下面是执行结果:
Book [Being A Great Cook] contained true
Book [Mountain Climbing] contained false

上面的例子比较简单,就不多做介绍了。

虽然我们不会经常用到BloomFilter,但是我们还是应该将它放到我们常用java工具库中。

Optional

null objects是比较棘手的一个问题,有很大一部分问题,都是由于我们认为一个方法返回的值可能不是null,但是我们经常惊讶的发现她居然是是null,为了解决这个问题,Guava中有这样的一个类,叫Optional, Optional是一个不可变对象,可能会包含一个对象引用,也可能不包含对象引用, 一个好的使用Optional类的方式是让方法的返回值为Optional,通过这种方式,我们可以强制调用方认为方法的返回值可能是不存在的。必须采取措施防止这种情况。

创建 Optioanal实例

Optional类是一个抽象类,我们可以直接继承,也有一些静态方法我们可以使用去创建Optional实例。
1. Optional.absent()返回一个空的Optional实例
2. Optional.of(T ref) 返回一个包含Type ref的Optioanal实例
3. OPtioanal.fromNullable(T ref) 如果 ref不为null,那么返回一个包含 Type t的Optional实例,否则返回一个空的Optional实例

让我们来看一些简单的例子:

@Test
public void testOptionalOfInstance(){
TradeAccount tradeAccount = new
TradeAccount.Builder().build();
Optional<TradeAccount> tradeAccountOptional =
Optional.of(tradeAccount);
assertThat(tradeAccountOptional.isPresent(),is(true));
}

在这个例子中,我们使用Optional.of方法,返回一个包含给定对象的Optional实例,我们通过调用isPresent方法来确认包含的对象是否存在。
更有趣的Optional.fromNullable例子如下:

@Test(expected = IllegalStateException.class)
public void testOptionalNull(){
Optional<TradeAccount> tradeAccountOptional =
Optional.fromNullable(null);
assertThat(tradeAccountOptional.isPresent(),is(false));
tradeAccountOptional.get();
}

在上面的单元测试的例子中,我们使用Optional.fromNullable()静态方法,这个例子中,我们也返回了一个Optional实例,这次我们调用isPresent方法,期望得到的结果为false,调用get方法会抛出IllegalStateException.
总的来说,使用Optional类似的我们必须处理Null值的情况

Throwables

Throwables类包含了很多静态方法区处理在java中必然会遇到的 java.lang.Throwable,Errors和Exceptions错误。有的时候有这样的一个工具类去处理异常堆栈是什么方便的。 而Throwables类刚好提供了这样的工具。 下面我们将去看下面两个比较特别的方法:

获取异常链

Throwables.getCausalChain 方法返回一个Throwable对象集合。从堆栈的最顶层依次到最底层,下面是一个比较好的例子:

@Test
public void testGetCausalChain() {
ExecutorService executor =
Executors.newSingleThreadExecutor();
List<Throwable> throwables = null;
Callable<FileInputStream> fileCallable = new
Callable<FileInputStream>() {
@Override
public FileInputStream call() throws Exception {
return new FileInputStream("Bogus file");
}
};
Future<FileInputStream> fisFuture =
executor.submit(fileCallable);
try {
fisFuture.get();
} catch (Exception e) {
throwables = Throwables.getCausalChain(e);
}
assertThat(throwables.get(0).getClass().
isAssignableFrom(Execution
Exception.class),is(true));
assertThat(throwables.get(1).getClass().
isAssignableFrom(FileNotFo
undException.class),is(true));
executor.shutdownNow();
}

上面的这个例子中,我们创建了一个Callable实例期望返回一个FileInputStream对象,但是我们故意写了一个文件不存在的地址,这样就会出现FileNotFoundException。 当我们调用get方法时,出现了异常,我们调用Throwables.getCausalChain方法获取具体的异常,首先第一个是ExecutionException,第二个是FileNotFoundException.类似这样我们就得到所有的异常类型,这样我们就可以处理感兴趣的异常。

获取根异常

Throwables.getRootCause方法接受一个Throwable实例,返回异常对象的Root Cause. 下面是具体的例子:

@Test
public void testGetRootCause() throws Exception {
ExecutorService executor =
Executors.newSingleThreadExecutor();
Throwable cause = null;
final String nullString = null;
Callable<String> stringCallable = new Callable<String>() {
@Override
public String call() throws Exception {
return nullString.substring(0,2);
}
};
Future<String> stringFuture=
executor.submit(stringCallable);
try {
stringFuture.get();
} catch (Exception e) {
cause = Throwables.getRootCause(e);
}
assertThat(cause.getClass().isAssignableFrom(NullPointerExcep
tion.
class),is(true));
executor.shutdownNow();
}

上面的这个例子中我们通过getRootCause() 获取到了根异常

总结

在这一章中我们覆盖到了一些非常有用的类,虽然它们不是经常的使用到,但是一旦需要,就会感觉非常的方便,首先我们学了Hash函数,和Hashing类提供的一个有用的工具, 接着我们学习了利用Hash函数构造的一个比较好用的数据结构BloomFilter. 我们还学习了Optional类,使用这样的类可以使我们的代码更加强壮,避免出现Null值引起的异常,最后我们学习了Throwables类,Throwables类包含了一些有用的静态方法,可以方便的处理代码抛出的异常。

时间: 2024-08-30 15:10:23

Guava翻译之Small Function的相关文章

guava翻译系列之Cache

Guava Cache 在软件开发的过程,缓存是一个非常重要的话题. 在稍微复杂的开发过程中,我们基本上是不可能不使用到缓存的. 至少我们会使用Map去存储一些东西. 这其实就是一个最简单的缓存. Guava给我们提供了比简单的使用HashMap更强大更灵活的功能,但是和专业的缓存工具相比,(EHCache,Memcached)功能还有些不足, 那么这一章,我们将覆盖Guava cache的下面几个方面: -- 使用MapMaker类创建ConcurrentMap实例 -- 使用CacheBui

Guava翻译之Functional

使用guava进行函数式编程 在这一章,我们将关注使用guava将我们的开发变得更加容易,我们下面将学习具体的接口和类让我们的程序的扩展性,健壮性更好. 我们这一章里面将覆盖一下几个主题: Function 接口: 介绍怎样在java中使用函数式编程,并且讨论一下函数式编程的最佳实践 Functions 类: Functions 是一个包含了一些实现Function接口的静态方法的集合 Predicate 接口:Predicate 接口是计算一个对象是否满足条件,如果满足条件则返回True pr

Guava翻译之Strings

使用Guava处理Strings 不管你更喜欢使用哪种语言,程序员都必须和字符串打交道,有时候这是无趣的并且容易出错的. 有的时候,我们需要从文件或者数据库的表中读取数据,并格式化这些数据. 或者为了满足业务需求我们需要排序等操作,幸运的是,Guava给我们提供一些非常有用的类,可以帮助我们处理Strings类更加容易.这些类是: CharMatcher Charsets Strings 下面让我们看一下在我们的代码中怎么样是使用这些类. 使用Charsets类 在Java平台下有个6个标准的字

guava翻译系列之Collections

引言 集合对于任何一门语言都是必须的.没有集合我们写不出一些复杂的逻辑.Guava继承扩展了Google Collections的一些功能. 从 com.google.common.collect包下面的类的数量,我们就可以看出Collections的重要性. 虽然已经有了这么多的工具类,但是还是有很多场景我们没有覆盖到,我们希望我们能够覆盖到日常使用的哪些. 下面我们就在每天的编程中经常会使用的类做一下介绍. 这一章节中我们将涉及以下几个方面: lists,maps,sets 等这些包含非常有

Guava翻译系列之File

使用guava处理文件 读写文件是一个程序员的核心能力! 令人意外的事,虽然java有非常丰富的并且强壮的I/O接口,但是却不怎么好用. 虽然在java7中已经有了一些改善. 但是我们还是要学一下guava的I/O相关的工具. 这一章我们要学习一下内容: -- 使用Files类处理文件的移动和复制,或者从文件中读取内容到字符串中 -- Closer 类 给我们提供非常简洁干净的方式去确保文件被正确关闭 -- ByteSource 和 CharSource 类,是inputStream和reade

Guava翻译系列之EventBus

EventBus 类解析 当我们开发软件时,各个对象之间的数据共享和合作是必须的. 但是这里比较难做的是 怎样保证消息之间的传输高效并且减少各个模块之间的耦合. 当组件的职责不清楚时,一个组件还要承担另一个组件的职责,这样的系统我们就认为是高耦合. 当我们的系统变得高耦合时,任何一个小的改动都会对系统造成影响. 为了解决设计上的问题,我们设计了基于事件的设计模型. 在事件驱动编程模型中,对象可以发布/订阅 事件. 事件监听者就是监听事件的发生,我们在第六章中已经看到过RemovalListene

guava翻译系列之Joiner

基本的guava工具 在前面的章节,我们已经讨论了什么是GUAVA和怎样去安装GUAVA,在这一章我们将开始使用guava,我们将展示guava提供的基本功能,并且了解一下这些基本功能是怎样帮助我们简化日常工作遇到的的问题 在这一章节中,我们将覆盖一下几个方面的内容: 使用Joiner Class 将字符串以指定的分隔符连接起来. 我们也会涉及到使用MapJoiner Splitter Class,和Joiner的作用相反,将一个字符串以给定的分隔符分隔开 使用CharMatcher 和 Str

guava翻译系列之Splitter

使用Splitter类 另一个常见的操作就是解析一个以固定分隔符分隔的字符串,并返回一个包含这个String的数组,如果你需要去阅读一个text文件,你会经常要处理这种情况.但是String.split方法还有一些可以改进的地方,就像下面的例子所展示的:String testString = "Monday,Tuesday,,Thursday,Friday,,"; //parts is [Monday, Tuesday, , Thursday,Friday] String[] part

nodejs简单实现中英文翻译

  这篇文章主要介绍了nodejs简单实现中英文翻译的方法和示例,虽然还存在着不小的问题,但是也算是基本能用了,这里推荐给大家. 帮以前同事解决一个需求,中文项目 翻译 英文项目~~~ 考虑到具体实现方面的问题,如果智能的话,肯定是要做中文的语法分析,不过感觉这个有难度. 所以最后的方案是遍历文件,将中文短语匹配出来,再进行人工翻译,将中文短语替换成翻译的内容.当然后期还是需要人工再检验下,毕竟代码中的中文,可能会影响到相关的程序. 这个问题,明显涉及到 多线程,文件读写,第一时间就想到的是 n