1-使用 GOOGLE COLLECTIONS,GUAVA,STATIC IMPORTS 编写漂亮代码
写在前面:
之前在一个项目中用到过guava,当时匆匆用,也没细研究,今天偶然在occhina看到这个系列教程的翻译,感受不错,介绍得还比较全面,就一口气全看完了,但看到第四节,发现还没翻译,因而本身就硬着头皮看了英文,发现也能看懂大概意思,就顺手翻译了一下,也算是为开源事业作点儿贡献吧。把文章转到本身博客记录一下。
附:
Guava API
Guava 用法整理git
本文转自网络,若有侵权,请联系本人删除! 原文 译文程序员
这个夏天的早些时候,我已经极力向个人全部同事推荐了 Google Collections 。 Kevin Bourrillion说他的一个同事告诉他“没有使用Google Collections前编程就像把一只手绑在背后”。
我灰常赞成Kevin的这个同事!
可能文章的标题有点奇怪。我指的是“编写漂亮代码”。我猜我应该说“简洁的”Java代码,可是,它们意思可不同(译者注:漂亮的代码看着很爽,简洁的不必定爽)。github
在我准备开始卖力的吆喝这个我最最喜欢的Java类库前,我有几个额问题要问问你:
多少次你写了像下面同样的代码:编程
1 |
Map<String, Map<Long, List<String>>> map = new HashMap<String, Map<Long,List<String>>>(); |
或者像这样的不堪入目的代码:数组
3 |
int compareTo = Integer.valueOf(a).compareTo(Integer.valueOf(b)); |
或者有不少的if和else :-(
又有多少次你写了像下面这样的代码,只为了从一个文件中读一点点东西?:网络
01 |
File file = new File(getClass().getResource( "/test.txt" ).getFile()); |
02 |
BufferedReader reader; |
05 |
reader = new BufferedReader( new FileReader(file)); |
08 |
line = reader.readLine(); |
12 |
text += line.trim() + "\n" ; |
16 |
} catch (FileNotFoundException e1) { |
18 |
} catch (IOException e) { |
好吧,我想说… 这是什么TM玩意?咱们已经有Apache Commons Collections不少年了。那为何咱们还须要另一个collections库呢?我看过不少像这样的评论:数据结构
“任何有一段时间开发经验的Java程序员都会积累这些类型的实用的工具类”架构
好吧,是的,这对于大多数开发者来讲多是(应该是)对的。可是,有太多理由来摆脱垃圾代码和重用漂亮的工具类!在这个博客里,我将要告诉你一些的确引发我求知欲-让我沉溺于其中的事情,那就是Google Collections。
正如Jared Levy 曾说过的:
这个库简化了你的代码,使它易写、易读、易于维护。它能提升你的工做效率,让你从大量重复的底层代码中脱身。
此外,我会给大家展现一下Guava里面包含的不少很是酷的功能,让大家看看如何用它来写出更漂亮的代码,一旦看到这些,大家就会和之前那种僵化的使人讨厌的写代码方式说拜拜了。
Google Guava 是 Google 为 Java 1.6 编写的核心库。它仍然不是一个很成熟的库,在将来几个月还将不断的变化。Google Collections 将在 1.0 版本发布时将成为 Guava 的一部分。Guava (和 Google Collections) 已经率先被不少 Google 开发者使用。支持该项目的开发者有 Kevin Bourrillion, Jared Levy, Crazy Bob Lee, Josh Bloch(!) (Google 的 Java 首席架构师) 和 limpbizkit (咱们找不到这家伙的真实姓名). Google Collections 在 2007 年就已经有了,但 Guava 是在 2009年9月推出的。
做为这个系列的博客,我将向你介绍 Google Collections 并告诉你使用 Guava 和 Google Collections 能为你带来什么好处。包括代码量的减小以及新的更快的数据结构。在第二部分咱们将深刻探讨 Guava 和 Collections 的一些高级特性。
一、Google Collections一览
显然一篇博文不能深刻地覆盖Google Collections的方方面面,因此我决定把时间放在我平常编码中使用到的基础且不失强大的特性上,首先,不要这么作:
1 |
Map<String, Map<Long, List<String>>> map = new HashMap<String, Map<Long,List<String>>>(); |
要这么作:
1 |
Map<String, Map<Long, List<String>>> map = Maps.newHashMap(); |
或者更甚者直接使用静态导入:
1 |
Map<String, Map<Long, List<String>>>map = newHashMap(); |
很棒,不是吗?多亏有泛型和写Collections的哥们提供给咱们的这些方便工厂方法,咱们再也不须要写一些Java自己应该自带的东西。好吧,我知道这些会是JDK 7里的一部分,那固然很好,不过Google Collections如今就有这些功能。
相似于com.google.common.collect.Maps提供的这些静态工具方法,Lists和Sets也有:
还有更多! 本身去看看吧![http://code.google.com/p/google-collections/]
二、操做lists和maps
当你在写单元测试时,常常会构造一些测试数据,多是list、map、set等,对于一些像我同样草率的人来讲,测试代码中会常常看到相似下面的语句:
1 |
List<String> list = new ArrayList<String>(); |
其实我也知道,这几行代码看起来很烂,我只是想用一些测试数据构造一个不可变的list而已,我但愿能像下面这样写一行代码搞定这些。。如何办到?好吧,这很简单!
1 |
ImmutableList<String> of = ImmutableList.of( "a" , "b" , "c" , "d" ); |
Map也同样
1 |
ImmutableMap<String,String> map = ImmutableMap.of( "key1" , "value1" , "key2" , "value2" ); |
我如今慢慢的愈来愈喜欢这种简单而又有效的写代码的方式,我还想缩短一下上面的代码,可是因为ImmutableList和ImmutableMap都有of方法,不能使用静态导入。不过有一个变通的方法就是说在咱们本身的集合工具中包装这些建立工厂方法,那么对于不可变的list和map我只须要简单的这么写就好:
1 |
ImmutableMap<String,String> map2 = mapOf( "key1" , "value1" , "key2" , "value2" ) |
或者
1 |
ImmutableList<String> list2 = listOf( "a" , "b" , "c" , "d" ); |
并且若是我想构造填充一个ArrayList(或者一个HashMap),我能够这样:
1 |
ArrayList<String> list3 = arrayListOf( "a" , "b" , "c" , "d" ); |
两种方式均可以,选择权在你手上,很明显,这种较以前面的方式更灵活优雅一些,你说呢? 除去可使用方便干净的方式来建立并填充集合类,咱们也提供了不少额外的工具方法,好比过滤,对set取交集和并集,排序等等一些更优雅的方法,我将会在第二部分中讲解它们。
三、静态导入和Eclipse模板
在这以前,我想先告诉你我在写代码时,是若是借用Eclipse的Code模板功能更高效的使用集合类的。(我假设你了解在IDEA或者其余IDE中也存在相似的功能,你能够根据这部分叙述作出相应的扩展或变通)
做为一个Eclipse的用户,我很是喜欢快捷键(参照这篇介绍Eclipse快捷键的博文:MouseFeed and how you can easily learn Eclipse shortcuts)
OK,Ctrl+space是大家的好朋友!他也是代码自动补全的快捷键。(译者注:固然。在中国,因为这个快捷键和切换输入法冲突,通常都设置为了“Alt+/”做为代码补全的快捷键)
在Eclipse下你能够建立一个模板来绑定到一个自动补全的快捷关键字上,这也是魔力所在!
相对于键入Maps.newHashMap()来建立一个HashMap,或者干脆使用静态导入,就能够简单的键入newHashMap()来建立一个HashMap,我只是简单的敲入newH,按下ctrl+space,见证奇迹的时刻到了!
在Eclipse菜单栏选择Window -> Preferences,进入Java -> Editor -> Templates,点击“New”。Name处就是你想敲入的快捷关键字,我一般命名为个人方法名,好比在这里,就是“newHashMap”,加上你喜欢的描述,好比”Import static Maps.newHashMap“,并增长下面的内容:
1 |
${:importStatic(com.google.common.collect.Maps.newHashMap)}newHashMap();${cursor} |
以上就是建立快捷补全的所有步骤了,如今去为你经常使用到的全部方法添加模板吧!
四、Guava蜻蜓点水
最后,但并不是不重要,我将向你展现一下若是使用Guava来处理本文开头留下来的两个问题:
1.从文件中按行读取内容:
1 |
File file = new File(getClass().getResource( "/test.txt" ).getFile()); |
2 |
List<String> lines = null ; |
4 |
lines = Files.readLines(file, Charsets.UTF_8); |
5 |
} catch (IOException e) { |
2.比较两个基本类型:
1 |
int compare = Ints.compare(a, b); |
3.把一个List转换为int数组:
1 |
List<Integer> list = listOf( 1 , 2 , 3 , 4 ); |
2 |
int [] array2 = Ints.toArray(list); |
Guava为咱们提供了对Core Java类库全面的扩展,咱们可使用com.google.common.primitices包下的Ints,Doubles,Floats,Shorts,Bytes以及Bools等工具类操做基本类型的数据;com.google.common.io包提供了操做streams,buffers以及files等等,并且并发包中提供了一些像Futures,Callables以及Executors等方便的工具类来减轻咱们写并发代码的痛苦。除此以外,Guava提供了对Collections的加强,以及很是优雅的CharMatcher、Joiner以及Splitter类,这些类我将在下篇博文中提到。
能够从这里得到源码:
下次咱们会深刻Guava 的高级功能,探索一下集合的用法,看看怎样经过Multimap使java的功能更增强大,如何用mapping功能来转换集合。请拭目以待。若是大家也用过Guava或者Google Collections请分享一下大家的心得。
2-深刻探索 GOOGLE GUAVA 库
在这个系列的第一部分里,我简单的介绍了很是优秀的Google collections和Guava类库,并简要的解释了做为Java程序员,若是使用Guava库来减小项目中大量的样板代码。在这篇博文中咱们将深刻挖掘Guava提供的更高级的特性。 咱们将深刻挖掘Guava库,并了解一下优雅的CharMatcher类、Joiner以及Splitter类,以及在处理Java基本类型时Guava给咱们带来的别的工具类。
一、The Guava CharMatcher
CharMatcher 能够很是方便地添加到你的java工具箱中。有些人形容它:“像打了兴奋剂的StringUtils”:p 你可使用预先设定好的常量,好比CharMatcher.WHITESPACE, CharMatcher.JAVA_DIGIT 或者CharMatcher.ASCII,此外你还有不少简便的工厂方法如CharMatcher.is(‘aaa’), CharMatcher.isNot(‘bbb’), CharMatcher.oneOf(‘abcd’).negate(),甚至更复杂一些好比:
1 |
CharMatcher.inRange( 'a' , 'z' ).or(inRange( 'A' , 'Z' )); |
固然你能够继承它而后实现方法 matches(char c)。你能够把 Google Collection中都创造实现一遍(固然下次咱们会覆盖到)!
若是你想从字符串中获得全部的数字,那么你能够这样:
1 |
String string = CharMatcher.DIGIT.retainFrom( "some text 89983 and more" ); |
若是你想把字符串中的数据都去掉,能够以下:
1 |
String string = CharMatcher.DIGIT.removeFrom( "some text 89983 and more" ); |
还有好多匹配的方法:
matchesAllOf(CharSequence)
atchesAnyOf(CharSequence)
matchesNoneOf(CharSequence)
除了indexIn, lastIndexIn and countIn这些方法,还有不少trimming, replacing and collapsing相关的方法。 更多信息查看Java doc.
二、Joiner and Splitter
目前Joiner仍是Collections 的一部分,Splitter 已经加入了Guava (不过一旦Collections 发布1.0版本,那么它们会一块儿加入到Guava)。
能够这么使用Joiner:
1 |
String[] subdirs = { "usr" , "local" , "lib" }; |
2 |
String directory = Joiner.on( "/" ).join(subdirs); |
或者这样:
1 |
int [] numbers = { 1 , 2 , 3 , 4 , 5 }; |
2 |
String numbersAsString = Joiner.on( ";" ).join(Ints.asList(numbers)); |
得益于Guava对基本型的支持,能够很方便这么处理:
1 |
String numbersAsStringDirectly = Ints.join( ";" , numbers); |
对于字符串,咱们能够直接进行分割,可是这样作多少有些奇怪, Splitter 提供了更多的操做,并且更加健壮。字符创分割一般返回的是一个数组而 Splitter 返回的是一个迭代 Iterable。
1 |
Iterable split = Splitter.on( "," ).split(numbsAsString); |
你能够简单地分割字符串:
1 |
String[] splitRegular = testString.split( "," ); |
可是当须要处理下面这样的字符串时:
1 |
String testString = "foo , what,,,more," ; |
输出结果是:
‘foo ‘
‘ what’
”
”
‘more
这样的结果也许还能够,可是Splitter容许咱们对分割结果作更多的控制:
1 |
Iterable<String> split = Splitter.on( "," ).omitEmptyStrings().trimResults().split(testString); |
获得的结果是 foo’,'what’,'more’
Joiner和Splitter都是可配置的,甚至你能够把Joiner使用在map中。
三、Working with primitives cont’d
在博客的第一部分,我简单提到了基本型的用法。Guava已经提供了诸如Ints.compare(a, b)和Ints.toArray(list)。
让我介绍Guava 关于基本型提供的更多的一些用法吧。
假如我有一个整型数字数组,咱们想知道数组中是否有特定的整型数字。传统的写法以下:
1 |
int [] array = { 1 , 2 , 3 , 4 , 5 }; |
使用Guava,咱们能够以下:
1 |
boolean contains = Ints.contains(array, a); |
一样,其余类型的基本型数组也能够这么来作。咱们甚至能够直接对数组作以下的事:
1 |
int indexOf = Ints.indexOf(array, a); |
2 |
int max = Ints.max(array); |
3 |
int min = Ints.min(array); |
4 |
int [] concat = Ints.concat(array, array2); |
在这个系列的下一章咱们将关注下 Google Collections library包的更高级特性如:Functions, Filtering and Ordering!欢迎继续收看,请与咱们分享你的见解。
3-JAVA 的函数式编程,经过 GOOGLE COLLECTIONS 过滤和调用
在本系列博客的第1、二部分,我介绍了很是优秀的Google Collections和Guava包。本篇博客中咱们来看看如何使用Google Collections来作到过滤和排序功能。此外,我会带你看看Google Collections是如何使Java有一点点“functional(方法化)”的进步了。
一、Functions, Functions, Functions!!
Google Collections给咱们带来了一对很是优雅的东东,叫作:Functions and Predicates! 和你使用的scala同样有神奇的地方,如今你可使用在no-functional 的java身上了。你能够在com.google.common.base包里找到这些(更多)。
咱们将在下一部分过滤集合的时候谈到Predicates类,首先咱们先看一下Function的用法!
Google collections提供了Function接口,实际上,一个function就是从一个对象到另一个对象的转换变形。
像Lists和Maps这类的Collection工具类给咱们提供了转换的方法:
1 |
topMap = Maps.transformValues(fromMap, function); |
2 |
toList = Lists.transform(fromList, function); |
举个例子来讲,假设你有一个Map,key是物品,value是对应的价格,单位是欧元。那么,你有个需求是将里面的价格都转换为美圆,传统的作法是遍历整个Map,而后更新每一个value值,将价格转换为美圆价格,好麻烦...
有了Functions,世界一会儿变清净了...
1 |
Map usdPriceMap = Maps.transformValues(eurPriceMap, new Function() { |
2 |
double eurToUsd = 1.4888 ; |
3 |
public Double apply( final Double from) { |
4 |
return from * eurToUsd; |
好吧,你可能说匿名内部类搞的有点糟,由于 你可能想重用这个function---这里只是演示函数式的一些特色而已。
和这个相似的,咱们也可使用Functions来把一个对象转换成一个彻底不一样的对象,好比将一个整形转换为字符串。
咱们稍后再深刻Functions类,首先咱们浏览一下Multimap集合以及咱们若是使用一点function来转换一个集合。
二、使用条件过滤集合
我在Integrasco作数据工做时遇到的最多见的任务是过滤数据和对大集合进行排序。 简单起见,咱们假设你有一个姓名列表想要过滤(看起来有点幼稚):
1 |
List<String> names = listOf( "Aleksander" , "Jaran" , "Integrasco" , "Guava" , "Java" ); |
咱们可使用com.google.common.collect.Iterables和com.google.common.base.Predicates类来过滤例子中的列表,使过滤后的列表中只包含Aleksander或者Jaran:
如今我知道这听起来有点傻帽,可是你仍然能够实现本身的Predicates条件类,好比返回名字长度小于5的列表(我从codemonkeyism.com偷到了下面这个例子):
1 |
Iterable<String> filtered = filter(names, or(or(equalTo( "Aleksander" ),equalTo( "Jaran" )), lengthLessThan( 5 ))); |
这个例子返回的是Aleksander,Jaran以及Java(由于Java的长度小于5)。or条件的部分读起来有点绕,但我还能够忍受。equalTo以及or条件都是Predicates自带的方法,用起来很方便。
如今咱们来实现一个lengthLessThan的条件,咱们只须要这么作:
1 |
private static class LengthLessThanPredicate implements Predicate<String> { |
2 |
private final int length; |
3 |
private LengthLessThanPredicate( final int length) { |
6 |
public boolean apply( final String s) { |
7 |
return s.length() < length; |
把这个实如今你的工具类里包装一下就像这样:
1 |
public static Predicate<String> lengthLessThan( final int length) { |
2 |
return new LengthLessThanPredicate(length); |
关注一下Stephan的博文fluent interfaces for Google Collections --写的至关优雅~!
三、对集合排序
多亏有了java Collections类,咱们能够这么排序:
1 |
Collections.sort(List<T>, Comparator<? super T>) |
但有时候咱们想作更复杂一些的事情,好比合并多个Comparator或者咱们可能只是想要排序过的集合的一个视图,而不改变原来集合的顺序。
Google Collections给咱们提供了Ordering,让咱们更好地掌控排序。假如咱们有两个对Person类排序的comparator,一个是根据lastName排序,一个是根据firstName排序:
01 |
Comparator<Person> byLastName = new Comparator<Person>() { |
02 |
public int compare( final Person p1, final Person p2) { |
03 |
return p1.getLastName().compareTo(p2.getLastName()); |
08 |
Comparator<Person> byFirstName = new Comparator<Person>() { |
09 |
public int compare( final Person p1, final Person p2) { |
10 |
return p1.getFirstName().compareTo(p2.getFirstName()); |
那么,假如咱们如今想现根据last name排序,再根据first name排序,而后对排序结果反序,那咱们咱们须要作的是:
1 |
List<Person> sortedCopy = Ordering.from(byLastName).compound(byFirstName).reverse().sortedCopy(persons); |
并且,你甚至无需建立Comparator比较器,你能够直接扩展Ordering类!
1 |
List<Person> sortedCopy = orderByLastName.compound(orderByFirstName).reverse().sortedCopy(persons); |
四、继续过滤和排序
在这个系列的第一部分,Steve提到了在Scala中,你能够这么作:
1 |
people.filter(_.firstName == "Steve" ).sort(_.lastName < _.lastName) |
功能是说从people这个集合中筛选出firstName为“Steve”的,而且按他们的lastName属性排序。
从语法上来看,这行代码很是优雅!那么咱们也来看一下使用Google Collections时应该怎么作。Google Collections给咱们提供了前面提到的Iterables类,咱们可使用Iterables类来实现过滤和转换(你可使用Google Collections里的Collections2来实现一样的功能)。
那如今咱们就来看一下如何实现和Steve的那行Scala代码同样的功能,虽然看起来没有Scala实现的那么优雅,但倒是使用Predicates和Ordering类来实现上面功能的一种方式。
1 |
Ordering.from(byLastName).sortedCopy(filter(persons, withFirstName( "Steve" ))); |
虽然跟Scala提供的语法糖有点差距(很明显咱们仍是须要咱们的“withFirstName”条件谓词以及“byLastName”比较器),但至少这比咱们不使用Google Collections接近多了!(若是采用Stephen的流式接口的话,代码会更易读)。
Kevin Bourrillion在另外一篇关于使用Google Collections编写函数式的Java的文章中提到:
语法很烂。并且同时这在语言自己改变以前只是权宜之计,到那时咱们才真正的选择最佳的语法并开始真正的函数式编程。因此我还没决定我会投入多大的努力到Function/Predicate中去(这段真的很难翻译..我去..)
在下篇也是最后一篇关于Google Collections和Guava的博文中,咱们将看到若是使用Google Collections操做Set和Map,以及使用Preconditiones来作验证,并且咱们带你体验一下奇妙的Multimap集合类以及如何进行切分!拭目以待吧!
4-条件,多重映射和分片
在本系列博客的前三章,咱们大概介绍了Google的Guava类库和Collections类库,做为一名Java开发人员,相信你会从使用这些类库,进而来减小在你项目中使用样板文件的数量而获益。在本系列博客的最后一篇中,我将带你们来了解一个会让你彻底爱上并沉浸于其中的的集合工具类-Multimap。咱们将带你们了解下如何使用Google Collections的Preconditions来作校验,可是在此以前,先让咱们来了解下如何对Set和Map进行交集、并集和差集的运算。
一、Set的交集,并集和map的相关操做
有时,当咱们须要去对两个Set的取交集、并集和差集的时候,那是一件相关麻烦的事情,并且代码看起来很凌乱。常常状况,你会以一遍又一遍的循环来结束这种作法。可是若是咱们使用Google Collections提供的Sets类就能够垂手可得的完成这些操做,而且是彻底免费的!
01 |
HashSet setA = newHashSet( 1 , 2 , 3 , 4 , 5 ); |
02 |
HashSet setB = newHashSet( 4 , 5 , 6 , 7 , 8 ); |
04 |
SetView union = Sets.union(setA, setB); |
05 |
System.out.println( "union:" ); |
06 |
for (Integer integer : union) |
07 |
System.out.println(integer); |
09 |
SetView difference = Sets.difference(setA, setB); |
10 |
System.out.println( "difference:" ); |
11 |
for (Integer integer : difference) |
12 |
System.out.println(integer); |
14 |
SetView intersection = Sets.intersection(setA, setB); |
15 |
System.out.println( "intersection:" ); |
16 |
for (Integer integer : intersection) |
17 |
System.out.println(integer); |
From the Public Object blog:
“不一样于惯例,这些方法没有作任何的拷贝。相反,他们返回了表明了这两个集合的视图。 可是在有些状况下,这些拷贝又颇有用,咱们能够用immutableCopy类中提供的一个便利方法来实现拷贝”
一样地,对于Map而言,咱们能够像下面这样处理:
1 |
MapDifference differenceMap = Maps.difference(mapA, mapB); |
若是用MapDifference类,咱们还能够这样:
1 |
differenceMap.areEqual(); |
2 |
Map entriesDiffering = differenceMap.entriesDiffering(); |
3 |
Map entriesOnlyOnLeft = differenceMap.entriesOnlyOnLeft(); |
4 |
Map entriesOnlyOnRight = differenceMap.entriesOnlyOnRight(); |
5 |
Map entriesInCommon = differenceMap.entriesInCommon(); |
Thank you, Google Collections!
二、用Preconditions进行校验
早在初夏的时候,一个叫刚刚加入咱们的同事Bent André向我介绍了用Preconditions进行校验的想法,可是,直到最近我才发现Google Collections实际上已经包含了相应的实现(咱们本身也已经有了类似的实现)。
那么这个实现具体是什么样子的呢?校验就是要求咱们准确无误的作一些事情,一般状况下,它看起来就是相似下面这样的代码:
if (count <= 0) { throw new IllegalArgumentException("must be positive: " + count); }
咱们想校验调用咱们的方法的方法传参是否正确,若是错了,应该发出一个“他出错了”的警告信息。 JavaDoc中是这样解释的:
“Precondition异常一般表示被调用方法发生了一个错误。它告诉调用者不能用这种方式、这种参数或者其它来调用这个方法。Postcondition或其它运行失败是不会抛出这种类型的异常的。”
所以用Preconditions类或者静态导入,咱们就能够用下面这行简单的代码来替代上面那些的冗长的代码了:
1 |
checkArgument(count > 0 , "must be positive: %s" , count); |
一样,该类里还有其它相似的方法来检验状态和空值。
至关简洁,不是么?
“请注意这里是相反的表达(译者注:应该指的是checkNotNull中有not的意思,因此是相反的); 你用Preconditions声明什么是你指望返回true的,就好像你在Java中运用断言,或者在JUnit中调用assertTrue方法同样。”
From the excellent blog series on Google Collections over at Public Object:
“Preconditions提供对方法状态的校验。它能使你输入的参数如你所指望的的那样足够简洁,与Java内建的断言不一样的是Preconditions是一直开启的。”
下面是我在Integrasco上翻遍我全部的代码中,找到的一个比较好的例子:
1 |
public PostExample( final String title, final Date date, final String author) { |
用了Preconditions以后咱们再来看看代码变得有多工整:
1 |
public PostExample( final String title, final Date date, final String author) { |
2 |
this .title = checkNotNull(title); |
3 |
this .date = checkNotNull(date); |
4 |
this .author = checkNotNull(author); |
很简洁,不是么?
稍后咱们会发表一篇关于异常和校验的更详细的文章,敬请关注!
三、一个集合统治一切 – Multimap
我已经数不清我有多少次须要在一个Map中实现一个key对应多个value的需求,最终不得不以Map<K, List<V>>这种结构来实现的经历了。 而且用这种方式实现一键多值的需求还存在很大的争议,以下所示:
01 |
Map<Person, List<BlogPost>> map = new HashMap<Person, List<BlogPost>>(); |
03 |
public void addBlogPost( final Person author, final BlogPost blogPost) { |
04 |
List<BlogPost> blogPosts = map.get(author); |
05 |
if (blogPosts == null ) { |
06 |
blogPosts = new ArrayList<BlogPost>(); |
07 |
map.put(author, blogPosts); |
09 |
blogPosts.add(blogPost); |
别告诉你你历来没这样作过?利用Google Collections中的Multimap类咱们能够轻松实现上述需求,并且感受很时髦(我很开心我能够这样表达):
1 |
Multimap<Person, BlogPost> multimap = ArrayListMultimap.create(); |
3 |
public void addBlogPost( final Person author, final BlogPost blogPost) { |
4 |
multimap.put(author, blogPost) |
Whoop, whoop!
Google Collections提供了多种Multimaps的实现,若是你想防止出现键值对,能够用HashMultimap;若是你须要键值对按照天然顺序排列,你可使用TreeMultimap;甚至你想按插入顺序来遍历集合,LinkedHashMultimap能够知足你的需求。
Multimaps一样提供了不少有用的方法,详细内容能够参考Multimap 和 Multimaps的API文档!很激动是么?那就继续往下看吧...
四、分片集合
我管这节叫分片,相信不少人会以为叫咱们在以前文章叫提到的“过滤集合”更准确。可是当咱们把把Multimap的概念和功能相结合的时候,你就会意识到他到底有多强大了,而不只仅是过滤那么简单!
咱们假设咱们已经拥有了包含了一组map的list。list里的每个Map表明拥有指定属性的一个文档。这个Map看起来可能会是下面这个样子:
1 |
mapOf( "type" , "blog" , "id" , "292" , "author" , "john" ); |
即每一个Map中咱们拥有3个属性,他们分别是“type”、 “id”和“author”。
如上所示,全部咱们的List看起来应该是下面这个样子:
1 |
List<Map<String, String>> listOfMaps |
如今,咱们想把这个list根据所装载对象的类型不一样分红多个list,好比一个叫“blog”,一个叫“news”等等...
若是没有Google Collections这将是一场恶梦!咱们必须得先循环这个list,而后再分别检查每个map中的key,而后再把根据类型的不一样放入不一样的list中。但若是咱们不知道map里都有哪些类型,这个过程会更痛苦!
想不想和我一块儿来看看有没有轻松的办法解决?
用一点点Function的魔法加上Multimaps,我样能够以一种上至关优雅的方式来解决这个问题: