☕【Java技术指南】「Guava Collections」实战使用相关Guava不一般的集合框架
Google Guava Collections 使用介绍
简介
Google Guava Collections 是一个对 Java Collections Framework 增强和扩展的一个开源项目。由于它高质量 API 的实现和对 Java特性的充分利用,使得其在 Java 社区受到很高评价。笔者主要介绍它的基本用法和功能特性。
使用介绍
Google Guava Collections(以下都简称为 Guava Collections)是 Java Collections Framework 的增强和扩展。每个 Java 开发者都会在工作中使用各种数据结构,很多情况下 Java Collections Framework 可以帮助你完成这类工作。
但是在有些场合你使用了 Java Collections Framework 的 API,但还是需要写很多代码来实现一些复杂逻辑, 这个时候就可以尝试使用 Guava Collections 来帮助你完成这些工作。这些高质量的 API 使你的代码更短,更易于阅 读和修改,工作更加轻松。
目标读者
- 对于理解 Java 开源工具来说,本文读者至少应具备基础的 Java 知识,特别是 JDK5 的特性。
- 因为 Guava Collections 充分使用了范型,循环增强这样的特性。作为 Java Collections Framework 的增强,读者必须对 Java Collections Framework 有清晰的理解,包括主要的接口约定和常用的实现类。
- 并且 Guava Collections 很大程度上是帮助开发者完成比较复杂的数据结构的操作,因此基础的数据结构和算法的知识也是清晰理解 Guava Collections 的 必要条件。
项目背景
Guava Collections 是 Google 的工程师 Kevin Bourrillion 和 Jared Levy 在著名"20%"时间写的代码。当然作为开源项目还有其他的开发者贡献了代码。
在编写的过程中,Java Collections Framework 的作者 Joshua Bloch 也参与了代码审核和提出建议。
目前它已经移到另外一个叫 guava-libraries 的开源项目下面来维护。 因为功能相似而且又同是开源项目,人们很很自然会把它和 Apache Commons Collections 来做比较。
集合介绍
Immutable Collections: 还在使用 Collections.unmodifiableXXX() ? Immutable Collections 这才是真正的不 可修改的集合 l Multiset: 看看如何把重复的元素放入一个集合。
Multimaps: 需要在一个 key 对应多个 value 的时候 , 自己写一个实现比较繁琐,让 Multimaps 来帮忙
BiMap: java.util.Map 只能保证 key 的不重复,BiMap 保证 value 也不重复 l
MapMaker: 超级强大的 Map 构造类
Ordering class: 大家知道用 Comparator 作为比较器来对集合排序,但是对于多关键字排序 Ordering class 可以简化很多的代码
Immutable Collections: 真正的不可修改的集合
- 大家都用过Collections.unmodifiableXXX() 来做一个不可修改的集合。例如你要构造存储常量的Set。
Set<String> set = new HashSet<String>(Arrays.asList(new String[]{"RED", "GREEN"}));
Set<String> unmodifiableSet = Collections.unmodifiableSet(set);
每次调 unmodifiableSet.add() 会抛出个 UnsupportedOperationException。
如果有人在原来的set上add或者remove元素会怎么样?结果unmodifiableSet也是被 add 或者 remove 元素了。
构造这样一个简单的set写了两句长的代码。下面看看ImmutableSet 是怎么来做地更安全和简洁 :
ImmutableSet<String> immutableSet = ImmutableSet.of("RED", "GREEN");
- 而且试图调 add 方法的时候,它一样会抛出 UnsupportedOperationException,重要的是代码的可读性增强了不少,非常直观地展现了代码的用意。如果像之前这个代码保护一个set 怎么做呢?**
ImmutableSet<String> immutableSet = ImmutableSet.copyOf(set);
从构造的方式来说,ImmutableSet 集合还提供了 Builder 模式来构造一个集合 :在这个例子里面 Builder 不但能加入单个元素还能加入既有的集合。
Builder<String> builder = ImmutableSet.builder();
ImmutableSet<String> immutableSet = builder.add("RED").addAll(set).build();
Guava Collections 还提供了各种 Immutable 集合的实现
ImmutableList
ImmutableSet
ImmutableSet.of(4, 8, 15, 16, 23, 42);
ImmutableSet.copyOf(numbers);
- ImmutableSortedSet
- ImmutableMap
public static final ImmutableMap<String, Integer>
ENGLISH_TO_INT = ImmutableMap
.with("four", 4)
.with("eight", 8)
.with("fifteen", 15)
.with("sixteen", 16)
.with("twenty-three", 23)
.with("forty-two", 42)
.build();
ImmutableMap.of(1, "one", 2, "two");
- ImmutableSortedMap (one day)
Multiset: 把重复的元素放入集合
你可能会说这和 Set 接口的契约冲突,因为Set接口的 JavaDoc 里面规定不能放入重复元素。事实上,Multiset 并 没有实现 java.util.Set 接口,它更像是一个Bag。普通的 Set 就像这样 :[car, ship, bike],而 Multiset 会是这样 :
[car x 2, ship x 6, bike x 3]。譬如一个 List 里面有各种字符串,然后你要统计每个字符串在 List 里面出现的次数:
Map<String, Integer> map = new HashMap<String, Integer>();
for(String word : wordList){
Integer count = map.get(word);
map.put(word, (count == null) ? 1 : count + 1);
}
//count word “the” Integer count = map.get(“the”);
如果用 Multiset 就可以这样 :
HashMultiset<String> multiSet = HashMultiset.create();
multiSet.addAll(wordList);
//count word “the” Integer count = multiSet.count(“the”);
这样连循环都不用了,而且 Multiset 用的方法叫 count,显然比在 Map 里面调 get 有更好的可读性。
Multiset 还提供了 setCount 这样设定元素重复次数的方法,虽然你可以通过使用 Map 来实现类似的功能,但是程序的可读性比 Multiset 差了很多。
常用实现 Multiset 接口的类有:
- HashMultiset: 元素存放于HashMap
- LinkedHashMultiset: 元素存放于 LinkedHashMap,即元素的排列顺序由第一次放入的顺序决定
- TreeMultiset:元素被排序存放于TreeMap
- EnumMultiset: 元素必须是 enum 类型
- ImmutableMultiset: 不可修改的 Mutiset
- 看到这里你可能已经发现 Guava Collections 都是以 create 或是 of 这样的静态方法来构造对象。这是因为这些集合
- 类大多有多个参数的私有构造方法,由于参数数目很多,客户代码程序员使用起来就很不方便。而且以这种方式可以
- 返回原类型的子类型对象。另外,对于创建范型对象来讲,这种方式更加简洁。
看到这里你可能已经发现 Guava Collections 都是以 create 或是 of 这样的静态方法来构造对象。这是因为这些集合类大多有多个参数的私有构造方法,由于参数数目很多,客户代码程序员使用起来就很不方便。而且以这种方式可以
返回原类型的子类型对象。另外,对于创建范型对象来讲,这种方式更加简洁。
Multimap: 在 Map 的 value 里面放多个元素
Multimap就是一个 key 对应多个 value 的数据结构。看上去它很像 java.util.Map 的结构,但是 Muitimap 不是 Map,没有实现 Map 的接口。设想你对 Map 调了 2 次参数 key 一样的 put 方法,结果就是第 2 次的 value 覆盖
了第 1 次的 value。但是对 Muitimap 来说这个 key 同时对应了 2 个 value。所以 Map 看上去是 : {k1=v1, k2=v2,...},而 Muitimap 是 :{k1=[v1, v2, v3], k2=[v7, v8],....}。
- 举个记名投票的例子,所有选票都放在一个 List 里面,List 的每个元素包括投票人和选举人的名字。
我们可以这样写 :
//Key is candidate name, its value is his voters
HashMap<String, HashSet<String>> hMap = new HashMap<String, HashSet<String>>();
for(Ticket ticket: tickets){
HashSet<String> set = hMap.get(ticket.getCandidate());
if(set == null){
set = new HashSet<String>();
hMap.put(ticket.getCandidate(), set);
}
set.add(ticket.getVoter());
}
我们再来看看 Muitimap 能做些什么 :
HashMultimap<String, String> map = HashMultimap.create();
for(Ticket ticket: tickets){
map.put(ticket.getCandidate(), ticket.getVoter());
}
就这么简单! Muitimap 接口的主要实现类有:
- HashMultimap: key 放在 HashMap,而 value 放在 HashSet,即一个 key 对应的 value 不可重复
- ArrayListMultimap: key 放在 HashMap,而 value 放在 ArrayList,即一个 key 对应的 value 有顺序可重复
- LinkedHashMultimap: key 放在 LinkedHashMap,而 value 放在 LinkedHashSet,即一个 key 对应的 value 有顺序不可重复
- TreeMultimap: key 放在 TreeMap,而 value 放在 TreeSet,即一个 key 对应的 value 有排列顺序
- ImmutableMultimap: 不可修改的 Multimap
BiMap: 双向 Map
**BiMap 实现java.util.Map 接口。它的特点是它的value和它 key一样也是不可重复的,换句话说它的 key 和value是等价的,如果你往 BiMap 的 value 里面放了重复的元素,就会得到 IllegalArgumentException。 **
举个例子,你可能经常会碰到在 Map 里面根据 value 值来反推它的 key 值的逻辑:
for(Map.Entry<User, Address> entry : map.entreSet()){
if(entry.getValue().equals(anAddess)){
return entry.getKey();
}
}
return null;
- 如果把 User 和 Address 都放在 BiMap,那么一句代码就得到结果了: return biMap.inverse().get(anAddess);
- 这里的 inverse 方法就是把 BiMap 的 key 集合 value 集合对调,因此 biMap == biMap.inverse().inverse()。
BiMap的常用实现有:
- HashBiMap: key 集合与 value 集合都有 HashMap 实现
- EnumBiMap: key 与 value 都必须是 enum 类型
- ImmutableBiMap: 不可修改的 BiMap
MapMaker: 超级强大的 Map 构造工具
MapMaker 是用来构造 ConcurrentMap 的工具类。
为什么可以把 MapMaker 叫做超级强大?看了下面的例子你就知道了。首先,它可以用来构造 ConcurrentHashMap:
//ConcurrentHashMap with concurrency level 8
ConcurrentMap<String, Object> map1 = new MapMaker().concurrencyLevel(8).makeMap();
或者构造用各种不同 reference 作为 key 和 value 的 Map:
//ConcurrentMap with soft reference key and weak reference value
ConcurrentMap<String, Object> map2 = new MapMaker().softKeys().weakValues().makeMap();
或者构造有自动移除时间过期项的 Map:
//Automatically removed entries from map after 30 seconds since they are created
ConcurrentMap<String, Object> map3 = new MapMaker()
.expireAfterWrite(30, TimeUnit.SECONDS)
.makeMap();
或者构造有最大限制数目的 Map:
//Map size grows close to the 100, the map will evict
//entries that are less likely to be used again
ConcurrentMap<String, Object> map4 = new MapMaker()
.maximumSize(100)
.makeMap();
或者提供当 Map 里面不包含所 get 的项,而需要自动加入到 Map 的功能。这个功能当 Map 作为缓存的时候很有 用 :
//Create an Object to the map, when get() is missing in map
ConcurrentMap<String, Object> map5 = new MapMaker().makeComputingMap(
new Function<String, Object>() {
public Object apply(String key) {
return createObject(key);
}});
这些还不是最强大的特性,最厉害的是 MapMaker 可以提供拥有以上所有特性的 Map:
//Put all features together!
ConcurrentMap<String, Object> mapAll = new MapMaker()
.concurrencyLevel(8)
.softKeys()
.weakValues()
.expireAfterWrite(30, TimeUnit.SECONDS)
.maximumSize(100)
.makeComputingMap(
new Function<String, Object>() {
public Object apply(String key) {
return createObject(key);
}});
Ordering class: 灵活的多字段排序比较器
要对集合排序或者求最大值最小值,首推 java.util.Collections 类,但关键是要提供 Comparator 接口的实现。假设有 个待排序的 List,而 Foo 里面有两个排序关键字 int a, int b 和 int c:
Collections.sort(list, new Comparator<Foo>(){
@Override
public int compare(Foo f1, Foo f2) {
int resultA = f1.a – f2.a;
int resultB = f1.b – f2.b;
return resultA == 0 ? (resultB == 0 ? f1.c – f2.c : resultB) : resultA;
}});
这看上去有点眼晕,如果用一串 if-else 也好不到哪里去。看看 ComparisonChain 能做到什么 :
Collections.sort(list, new Comparator<Foo>(){
@Override
return ComparisonChain.start()
.compare(f1.a, f2.a)
.compare(f1.b, f2.b)
.compare(f1.c, f2.c).result();
}});
如果排序关键字要用自定义比较器,compare 方法也有接受 Comparator 的重载版本。譬如 Foo 里面每个排序关键字都已经有了各自的 Comparator,那么利用 ComparisonChain 可以 :
Collections.sort(list, new Comparator<Foo>(){
@Override
return ComparisonChain.start()
.compare(f1.a, f2.a, comparatorA)
.compare(f1.b, f2.b, comparatorB)
.compare(f1.c, f2.c, comparatorC).result();
}});
Ordring 类还提供了一个组合 Comparator 对象的方法。而且 Ordring 本身实现了 Comparator 接口所以它能直接作 为 Comparator 使用:
Ordering<Foo> ordering = Ordering.compound(Arrays.asList(comparatorA, comparatorB, comparatorc));
Collections.sort(list, ordering);
过滤器(stream-filter)
利用 Collections2.filter() 方法过滤集合中不符合条件的元素。譬如过滤一个 List 里面小于 10 的
元素 :
Collection<Integer> filterCollection =
Collections2.filter(list, new Predicate<Integer>(){
@Override
public boolean apply(Integer input) {
return input >= 10;
}});
当然,你可以自己写一个循环来实现这个功能,但是这样不能保证之后小于 10 的元素不被放入集合。
filter 的强大之 处在于返回的 filterCollection 仍然有排斥小于 10 的元素的特性,如果调 filterCollection.add(9) 就会得到一个IllegalArgumentException。
转换器(Stream-map)
利用 Collections2.transform() 方法来转换集合中的元素。譬如把一个 Set 里面所有元素都转换成 带格式的 String 来产生新的 Collection:
Collection<String> formatCollection =
Collections2.transform(set, new Function<Integer, String>(){
@Override
public String apply(Integer input) {
return new DecimalFormat("#,###").format(input);
}} );
总结
以上介绍了 Guava Collections 的一些基本的功能特性。你可以从 guava-libraries 的官方网站下载它的 jar 包和它其他的相关文档。如果你使用 Maven 来管理你的项目依赖包,Maven 中央库也提供了它版本的依赖。最后希望Guava Collections 使你的编程工作更轻松,更有乐趣。
使用
这个开源项目发布的 jar 包可以在它的官方网站内(http://code.google.com/p/guava-libraries/downloads/list)找到。
其下载的 zip 包中含有 Guava Collections 的 jar 包 guava-r09.jar 及其依赖包 guava-r09-gwt.jar,javadoc,源代码,readme 等文件。使用时只需将 guava-r09.jar 和依赖包 guava-r09-gwt.jar 放入 CLASSPATH 中即可。
如果您使用 Maven 作为构建工具的话可以在 pom.xml 内加入:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>xxx</version>
</dependency>
☕【Java技术指南】「Guava Collections」实战使用相关Guava不一般的集合框架的更多相关文章
- 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架
前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...
- JAVA之旅(十八)——基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用
JAVA之旅(十八)--基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用 JAVA把完事万物都定义为对象,而我们想使用数据 ...
- SpringBoot图文教程17—上手就会 RestTemplate 使用指南「Get Post」「设置请求头」
有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1-Spr ...
- Java已五年1—二本物理到前端实习生到Java程序员「回忆贴」
关键词:郑州 二本 物理专业 先前端实习生 后Java程序员 更多文章收录在码云仓库:https://gitee.com/bingqilinpeishenme/Java-Tutorials 前言 没有 ...
- ☕【Java技术指南】「并发编程专题」针对于Guava RateLimiter限流器的入门到精通(含实战开发技巧)
并发编程的三剑客 在开发高并发系统时有三剑客:缓存.降级和限流. 缓存 缓存的目的是提升系统访问速度和增大系统处理容量. 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题 ...
- ☕【Java技术指南】「编译器专题」深入分析探究“静态编译器”(JAVA\IDEA\ECJ编译器)是否可以实现代码优化?
技术分析 大家都知道Eclipse已经实现了自己的编译器,命名为 Eclipse编译器for Java (ECJ). ECJ 是 Eclipse Compiler for Java 的缩写,是 Jav ...
- ☕【Java技术指南】「并发原理专题」AQS的技术体系之CLH、MCS锁的原理及实现
背景 SMP(Symmetric Multi-Processor) 对称多处理器结构,它是相对非对称多处理技术而言的.应用十分广泛的并行技术. 在这种架构中,一台计算机由多个CPU组成,并共享内存和其 ...
- ☕【Java技术指南】「编译器专题」重塑认识Java编译器的执行过程(常量优化机制)!
问题概括 静态常量可以再编译器确定字面量,但常量并不一定在编译期就确定了, 也可以在运行时确定,所以Java针对某些情况制定了常量优化机制. 常量优化机制 给一个变量赋值,如果等于号的右边是常量的表达 ...
- ☕【Java技术指南】「OpenJDK专题」想不想编译属于你自己的JDK呢?(Windows10环境)
Win10下编译OpenJDK8 编译环境 Windows10专业版64位: 编译前准备 Tip: 以下软件的安装和解压目录尽量不要包含中文或空格,不然可能会出现问题 安装 Visual Studio ...
随机推荐
- Spring Boot中文文档(官方文档翻译 基于1.5.2.RELEASE)
作者:Phillip Webb, Dave Syer, Josh Long, Stéphane Nicoll, Rob Winch, Andy Wilkinson, Marcel Overdijk, ...
- mysql学习--MySQL存储引擎对比总结
一.存储引擎是什么 存储引擎是数据库的核心,对于mysql来说,存储引擎是以插件的形式运行的.MySQL默认配置了许多不同的存储引擎,可以预先设置或者在MySQL服务器中启用.你可以选择适用于服务器. ...
- SECURECRT 连接锐捷交换机CONSOLE
协议选择Serial,端口选择COM1.波特率设置为9600.RTS/CTS要把勾去掉(关闭流控功能)
- 基于FPGA的图像镜像
图像镜像,一种较为常见的图像处理操作,分为水平镜像.垂直镜像.对角镜像.水平镜像即处理后的图像与原图像关于垂直线对称,垂直镜像为处理后的图像与 原图像关于水平线对称,对角镜像则关于对角线对称. 关于低 ...
- HDFS学习总结之API交互
第一种.shell交互 官方文档:http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.7.0/hadoop-project-dist/ha ...
- Vulnhub -- DC1靶机渗透
信息收集 开启了 22端口,ssh服务 80,http服务 111,RPC服务 先观察80端口 发现采用的是Drupal的cms 目录爆破: 但是,没有找到可以直接利用的点 登录的页面进行SQL注入即 ...
- [考试总结]noip8
又是一个题的正解都没有打出来的一天 但是自己独创了 \(lca\) 的求法, 然而如果去掉求 \(lca\) 的过程,就不会 \(TLE\) 了. \(\huge{\text{囧}}\) 然后就是对性 ...
- JAVA学习笔记之基础概念(一)
一.Java 简介: Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称. 由 James Gosling和同 ...
- 嵌套div的onClick事件问题
嵌套div的onClick事件问题我在下面的代码中的外层div中加了onClick事件,这样当鼠标点击这个div的时候就会跳转了.但是我在图片上加了一些其他效果,所以当鼠标点击中间的img时不能触发跳 ...
- Go语言基础知识总结(持续中)
Go基础知识总结 变量声明 Go语言中的变量需要声明以后才可以使用(需要提前定义变量)并且声明后必须使用(不适用会报错) 标准声明 var 变量名 变量类型 example: var name str ...