Guava库学习:Guava 零碎知识

    这将是Guava库学习系列的最后一篇,但是仍然包含许多零零碎碎的知识。虽然不可能覆盖所有Guava涉及的知识,但我们会竭尽所能。本篇将会介绍一些Guava中有用的工具,并不需要再开一个系列。本篇学习的一些工具可能并不会经常使用,但当你需要时,它是必不可少的。接下来,开始本篇的学习。 本篇,我们将主要学习以下内容:Hashing、BloomFilter、Optional、Throwable。  

  • Hashing散列类包含静态实用方法获取HashFunction实例

  • BloomFilter数据结构,用于判断一个元素是否存在于一个Set集合中,BloomFilter数据结构有独特的属性,它可以明确返回一个元素不存在,不能确保一个元素肯定存在。

  • Optional类为我们提供了一种使用null引用的方式

  • Throwable类提供了一些静态实用方法处理Throwable的实例

    创建合适的Hash函数

      散列Hash函数在编程时很基础,它用于确定身份和检查重复。另外,它对于正确使用Java集合至关重要。散列函数工作原理是提供各种长度的数据并将它们映射为数字。因为我们打算将任意数据映射到数字,所以保证Hash函数的耐碰撞性是至关重要的。换句话说,就是我们要避免为不同的数据产生同样的编号。当然了要写一个优秀的哈希函数,最好是留给专家来做。幸运的是,通过Guava,我们不需要编写自己的Hash函数。Hashing类提供了静态的方法来创建HashFunction实例和一些需要注意的类型。

  校验总和Hash函数

      Guava提供了两种HashFunction类,实现众所周知的校验总和算法:Adler-32和CRC-32。通过如下代码,可以为HashFunction创建一个实例:

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

    上面,我们简单的做了一个Hashing类静态方法的调用,来说明所需的HashFuction的实现。

一般Hash函数

    接下来我们将调用一般的Hash函数,一般的散列函数非加密,适合用于基于散列的查询任务。第一个是murmur Hash算法,由Austin Appleby创建于2008年,其他一般的散列函数称为goodFastHash。下面来看怎样创建这些一般性Hash函数:

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

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

加密Hash函数

加密哈希函数用于信息安全,对加密Hash函数进行完整描述超出了本文的范围,这里只做简单的学习。一般来说,加密哈希函数有以下属性:

  • 数据的任何小变化,产生的Hash码都会发生大的变化

  • 通过反向工程,根据Hash code值推算出原始的数据是不可能的

    Guava提供了如下的方式创建加密Hash函数:

1HashFunction sha1 = Hashing.sha1(); 2HashFunction sha256 = Hashing.sha256(); 3HashFunction sha512 = Hashing.sha512();

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

Bloom Filter

   Bloom Filter是一个独特的数据结构,用来确定一个元素是否存在于一个集合中。有意思的一点是,它能准确的判断一个元素不存在,不能准确的判断元素存在。这种特性可以用在比较耗时的操作中,如磁盘检索。

    BloomFilter简述

    BloomFilter本质上是位向量。它以如下方式工作:

  1. 添加一个元素到filter中

  2. 将这个元素进行多次Hash运算,将得到的hash值的bit位设置为1

    当判断一个元素是否存在在set中时,按照同样的方法进行多次hash,并判断bit位设置的是1还是0。这就是BloomFilter确保一个元素不存在的过程。 如果bit位不为1,就说明这个元素不存在于集合中,相反,即使元素位都是为1,也不能确定元素存在于集合中,因为可能在之前已经发生了hash碰撞。在 学习Guava BloomFilter的创建和使用之前,我们需要了解如何得到对象的字节并读入BloomFilter进行hash运算。

Funnels 和 PrimitiveSinks

Funnel接口接收一个确定类型的对象,并将数据发送给PrimitiveSinks实例。PrimitiveSinks对象用来接收原始类型的数据。PrimitiveSinks实例将抽取hash运算所需的字节数。BloomFilter中使用Funnel接口来抽取那些在BloomFilter数据结构中等待hash的元素的字节。来看下面的例子:

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

    上面的例子中,我们创建了一个简单的Funnel实例来接收Book实例。需要注意的是,我们通过枚举实现了Funnel接口,这有助于保持BloomFilter的序列化,也需要Funnel实例是可序列化的。ISBN和Price被放入到PrimitiveSink实例作为Hash函数的入参。

创建BloomFilter实例

上面我们了解了如何创建Funnel实例,下面介绍如何创建BloomFilter实例:

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

上面的例子中,我们通过调用BloomFilter静态的create方法,传入Funnel实例和一个表示int值,这个值表示BloomFilter中使用hash函数的个数。如果使用的hash函数的个数大大超过了,假阳性的数量将大幅上升。我们来看一个创建BloomFilter实例的例子:

1@Test 2public void testBloomFilter() throws IOException { 3    File booksPipeDelimited = new File("src/books.data"); 4    List<Book> books = Files.readLines(booksPipeDelimited, 5            Charsets.UTF_8, new LineProcessor<List<Book>>() { 6                Splitter splitter = Splitter.on('|'); 7                List<Book> books = Lists.newArrayList(); 8                Book.Builder builder = new Book.Builder(); 9                public boolean processLine(String line) throws IOException { 10                    List<String> parts = Lists.newArrayList(splitter.split(line)); 11                    builder.author(parts.get(0)) 12                            .title(parts.get(1)) 13                            .publisher(parts.get(2)) 14                            .isbn(parts.get(3)) 15                            .price(Double.parseDouble(parts.get(4))); 16                    books.add(builder.build()); 17                    return true; 18                } 19                @Override 20                public List<Book> getResult() { 21                    return books; 22                } 23            }); 24    BloomFilter<Book> bloomFilter = BloomFilter.create(BookFunnel.FUNNEL, 5); 25    for (Book book : books) { 26        bloomFilter.put(book); 27    } 28    Book newBook = new Book.Builder().title("Test Cook Book 2").build(); 29    Book book1 = books.get(0); 30    System.out.println("book [" + book1.getTitle() + "] contained " + bloomFilter.mightContain(book1)); 31    System.out.println("book [" + newBook.getTitle() + "] contained " + bloomFilter.mightContain(newBook)); 32}

测试结果如下:

1book [Test Cook Book] contained true 2book [Test Cook Book 2] contained false

    在上面的例子中,我们通过Files.readLines方法以 | 为分隔符读取和使用文件,结合LineProcessor回调将每行的文本转换为Book对象。每一个Book对象都添加到List里面并返回。之后我们通过BookFunnel枚举和期望的hash次数 5,创建了一个BloomFilter实例。之后将所有的Book对象从list中添加到BloomFilter,最后,通过调用mightContain方法测试添加和未添加到BloomFilter的Book。

    虽然我们可能不需要经常使用BloomFilter,但在工作中这是一个非常有用的工具。

Optional

    空对象的处理比较麻烦,有很大一部分问题,都是由于我们认为一个方法返回的值可能不是null,但我们惊讶的发现对象居然为null,为了解决这个问题,Guava提供了一个Optional类,它是一个不可变对象,可能包含或不包含另一个对象的引用。如果Optional包含实例,它被认为是存在present,如果不包含实例,它被认为是缺席absent。Optional类比较好的使用方式是使用Optional作为方法的返回值。这样我们迫使客户端考虑返回值可能不存在,我们应该采取相应的措施防止此情况。

创建Optional实例

    Optional类是抽象类,我们可以直接继承,我们可以使用它提供的一些静态方法来创建Optional实例,例如:

  1. Optional.absent() ,返回一个空的Optional实例 

  2. Optional.of(T ref) ,返回一个包含Type ref的Optioanal实例 

  3. Optioanal.fromNullable(T ref) ,如果 ref不为null,那么返回一个包含Type ref的Optional实例,否则返回一个空的Optional实例

  4. Optional.or(Supplier<T> supplier),如果引用存在,返回此引用,否则,返回Supplier.get。

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

1@Test 2public void testOptionalOfInstance() { 3    Book book = new Book.Builder().build(); 4    Optional<Book> bookOptional = Optional.of(book); 5    assertThat(bookOptional.isPresent(), is(true)); 6}

在上面的单元测试中,我们使用了静态的Optional.of方法对传入的对象装饰后,返回一个Optional实例。我们通过调用isPresent方 法来断言包含的对象存在(为true)。更有趣的是通过如下方式使用Optional.fromNullable方法:

1@Test(expected = IllegalStateException.class) 2public void testOptionalNull() { 3    Optional<Book> bookOptional = Optional.fromNullable(null); 4    assertThat(bookOptional.isPresent(), is(false)); 5    bookOptional.get(); 6}

    在上面的单元测试中,我们通过fromNullable静态方法创建了Optional实例,同样我们也返回了Optional实例,这里我们断言调用 isPresent方法返回的是false。之后,由于没有实例存在,我们断言调用get方法会抛出IllegalStateExeption异常。 Optional.fromNullable是很好的方法,用来在调用返回结果之前装饰对象。Optional的真正重要性是,它对于返回值是否存在是没 有保证的,它迫使我们必须去处理Null值的情况。

Throwables

    Throwables类包含一些实用的静 态方法,用来处理在java中经常遇到的java.lang.Throwable、Errors 和 Exceptions错误。有的时候,有一个工具类去处理异常堆栈是很方便的,Throwables类给我们提供了方便。下面我们将介绍两个比较特别的方 法:Throwables.getCausalChain 和 Throwables.getRootCause。

获取Throwables异常链

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

1@Test 2public void testGetCausalChain() { 3    ExecutorService executor = Executors.newSingleThreadExecutor(); 4    List<Throwable> throwAbles = null; 5    Callable<FileInputStream> fileCallable = new Callable<FileInputStream>() { 6        @Override 7        public FileInputStream call() throws Exception { 8            return new FileInputStream("Bogus file"); 9        } 10    }; 11    Future<FileInputStream> fisFuture = executor.submit(fileCallable); 12    try { 13        fisFuture.get(); 14    } catch (Exception e) { 15        throwAbles = Throwables.getCausalChain(e); 16    } 17    assertThat(throwAbles.get(0).getClass().isAssignableFrom(ExecutionException.class), is(true)); 18    assertThat(throwAbles.get(1).getClass().isAssignableFrom(FileNotFoundException.class), is(true)); 19    executor.shutdownNow(); 20}

    在这个例子中,我们创建了一个Callable实例期望返回一个FileInputStream对象,我们故意制造了一个 FileNotFoundException。之后,我们将Callable实例提交给ExecutorService,并返回了Future引用。当我 们调用Future.get方法,抛出了一个异常,我们调用Throwables.getCausalChain方法获取到具体的异常链。最后,我们断言 异常链中的第一个Throwable实例是ExecutionException,第二个是FileNotFoundException。通过这个 Throwable的异常链,我们可以选择性的过滤我们想要检查的异常。

获取根异常

    Throwables.getRootCause方法接收一个Throwable实例,并返回根异常信息。下面是一个例子:

1@Test 2public void testGetRootCause() throws Exception { 3    ExecutorService executor = Executors.newSingleThreadExecutor(); 4    Throwable cause = null; 5    final String nullString = null; 6    Callable<String> stringCallable = new Callable<String>() { 7        @Override 8        public String call() throws Exception { 9            return nullString.substring(0, 2); 10        } 11    }; 12    Future<String> stringFuture = executor.submit(stringCallable); 13    try { 14        stringFuture.get(); 15    } catch (Exception e) { 16        cause = Throwables.getRootCause(e); 17    } 18    assertThat(cause.getClass().isAssignableFrom(NullPointerException.class), is(true)); 19    executor.shutdownNow(); 20}

    我们同样使用一个Callable 实例,并故意抛出一个异常,这次是NullPointerException。当我们通过返回的Future对象stringFuture调用get方法 捕获到相应的异常后,我们调用了Throwables.getRootCause方法,并将返回的Throwable对象赋值给cause变量,然后我们 断言根异常确实是NullPointerException。虽然这些方法不会取代日志文件中对异常堆栈信息的追踪,但我们可以使用这些方法在以后处理那些有价值的信息。

Summary

在本文中,我们介绍了一些非常有用的类,这些类可能并不经常被用到,但是在需要的时候,会变得很方便。首先我们学习了Hash函数和Hashing提供的 一些工具,之后我们利用hash函数构造了一个有用的数据结构BloomFilter。我们也学习了Optional类,使用它可以避免那些null值引 起的异常,让我们的代码变得更健壮。最后,我们介绍了Throwables,它包含一些有用的方法,能够方便的帮助我们处理程序中抛出的异常。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Python+Selenium自动化篇

本篇文字主要学习selenium定位页面元素的集中方法,以百度首页为例子。0.元素定位方法主要有:id定位:find\_element\_by\_id('')name定位:find\_element\_by\_name('')class定位:find\_element\_by\_class\_name(''