拼接guava是个风火轮之基础工具(1)-java开发java经验技巧

java开发工具  时间:2021-02-26  阅读:()

Gu ava是个风火轮Z基础工具(1)-编程开发技术

Guava是个风火轮之基础工具(1)

原文出处潘家邦的博客

・ 、 F4a

刖5

Guava是Java开发者的好朋友。虽然我在开发屮使用Guava很长时间了 Guava API的身影遍及我写的生产代码的每个角落但是我用到的功能只是

Guava的功能集中一个少的可怜的真子集更别说我一直没有吋间认真的去挖掘Guava的功能没有时间去学习Guava的实现。直到最近我开始阅读^GettingStarted withGoogle Gua va,感觉有必耍将我学习和使用Guava的一些东四记录下来。

Joiner

我们经常需要将几个字符串或者字符串数组、列表之类的东西拼接成一个以指定符号分隔各个元素的字符串比如把[1, 2, 3]拼接成“1 2 3” 。

在Python中我只需要简单的调用str. join函数就可以了就像这样。

' ' ・ join(map(str, [1, 2, 3] ) )

到了Java屮如果你不知道Guava的存在基木上就得手写循环去实现这个功能代码瞬间变得±1陋起來。

Guava为我们提供了一套优雅的API,让我们能够轻而易举的完成字符串拼接这一简单任务。还是上面的例子借助Guava的Joiner类,代码瞬间变得优雅起来。Join er. on(' ' ) . jo in(l, 2, 3) ;

被拼接的对象集可以是硬编码的少数几个对象可以是实现了Tterable接口的集合也可以是迭代器对象。

除了返回一个拼接过的字符串 Joiner述可以在实现了Appendable接口的对象所维护的内容的末尾追加字符串拼接的结果。

Str in gBuilder sb二new Str in gBuilder ( zresult:/z) ;

Joiner, on ( z “) . appendTo(sb, 1, 2, 3) ;

System.out.println(sb) ;//rcsult: 1 2 3

Guava对空指针冇着严格的限制如果传入的对象屮包含空指针Joiner会直接抛出NPEo与此同时 Joiner提供了两个方法让我们能够优雅的处理待拼接集合屮的空

指针。

如果我们希望忽略空指针那么可以调用skipNulls方法得到一个会跳过空指针的Joiner实例。如果希望将空指针变为某个指定的值那么可以调用useForNull方法指定用来替换空指针的字符串。

Joiner. on( ' ) ・ skipNulls() ・ join(l, null, 3) ;//l 3

Join er. on(' ' ) . useForNull ("Non e〃 ) .jo in(l, null, 3) ;//l None 3

需要注意的是,Joiner实例是不可变的,skipNulls和useForNul 1都不是在原实例上修改某个成员变量而是生成一个新的Joiner实例。

Joiner. Mapjoiner

Mapjoiner是Joiner的内部静态类用于帮助将Map对彖拼接成字符串。

Joiner. on( . withKeyValueSeparator( 〃二“) .join(ImmutableMap .of仃 2, 3,

4) ) ;//l二2#3二4withKeyValueScparator方法指定了键与值的分隔符同时返回一个Mapjoiner实例。有些家伙会往Map里插入键或值为空指针的键值对如果我们要拼接这种Map,千万记得要用useForNull对Mapjoiner做保护不然NPE妥妥的。

源码分析

源码來口Guava 1& 0 。 Joiner类的源码约450行其中大部分是注释、函数重载常用手法是先实现一个包含完整功能的函数然后通过各种封装把不常用的功能隐藏起来提供优雅简介的接口。这样子的好处显而易见用户可以使用简单接口解决80%的问题那些罕见而复杂的需求交给全功能函数去支持。

初始化方法

由于构造函数被设置成了私冇Joiner只能通过Joiner#on函数来初始化。最基础的Joiner#on接受一个字符串入参作为分隔符而接受字符入参的Joiner# 。 n方法是前者的重载内部使用StringttvalueOf函数将字符变成字符串后调用前者完成初始化。或许这是一个利于字符串内存回收的优化。

追加拼接结果整个Joiner类最核心的函数莫过于 〈A extends Appendable>Joiner#appendTo(A, lterator<?» ,一切的字符串拼接操作最后都会调用到这个函数。这就是所谓的全功能函数其他的一切appendTo只不过是它的重载,一切的join不过是它和它的重载的封装。public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throwsIOExccption {checkNotNull (appendable) ;if (parts. hasNext() ) {appendable. append(loString(parts. next () ) ) ;while (parts.hasNextO) { appendablc. append(scparator) ; appendable, append

(toString(parts. next () ) ) ;

}

}return appendable;

}

这段代码的笫一个技巧是使用if和while来实现了比较优雅的分隔符拼接避免了在末尾插入分隔符的尴尬第二个技巧是使用了自定义的toString方法而不是Object#toString来将对象序列化成字符串为后续的各种空指针保护开了方便之门。注意到一个比较有意思的appendTo重载。public final StringBuilder appendToCStringBuilder builder, Iterator<?> parts) {try {appendTo( (Appendable) builder, parts) ;

} catch (IOExccption impossible) {throw new AssertionError(impossible) ;

}return builder;

}

在Appendable接口屮 append方法是会抛出lOException的。然而StringBuilder虽然实现了Appendable,但是它覆盖实现的append方法却是不抛出lOException的。于是就出现了明知不可能抛异常却乂不得不去捕获异常的尴尬。

这里的异常处理手法十分机智异常变量命名为impossible,我们一看就明白这里是不会抛出IOExccption的。但是如杲catch块里面什么都不做又好像不合适于是抛出一个AssertionError,表示对于这里不抛异常的断言失败了。

另一个比较有意思的appendTo重载是关于可变长参数。public final <A cxtcnds Appcndablc> A appcndTo(

Aappendable, @Nullable Object first, @Nullable Object second, Object. . . rest)throws IOException {return appendTo(appendoble, iterable(first, second, rest) ) ;

}

注意到这里的iterable方法它把两个变量和一个数组变成了一个实现了Iterable接口的集合,手法精妙!private static Iterable<Object> iterable(final Object first, final Object second, final Object[] rest)

{ chcckNotNull (rest) ;return new AbstractList<Object>() {

©Override public int size() {return rest, length + 2;

}

©Override public Object get (int index) {switch (index) {case 0:return first;case 1 :return second;default:return rest[index - 2] ;

}

}

} ;

}

如果是我来实现可能是简单粗暴的创建一个ArrayList的实例然后把这两个变量一个数组的全部元索放到ArrayIJst里面然后返回。这样子代码虽然短了但是代价却不小为了一个小小的重载调用而产生了0(n)的时空复杂度。

看看人家G社的做法。要想写岀这样的代码需要熟悉顺序表迭代器的实现。迭代器内部维护着一个游标 cursoro迭代器的两大关键操作 hasNext判断是否还有没遍历的元素 next获取下一个元素它们的实现是这样的。public boolean hasNext() {return cursor != sizc() ;public E next () { checkForComodi fi cati on() ;try {int i = cursor;

E next = get (i) ;lastRet二i;cursor二i + 1;rcturn next;

} catch (IndexOutOfBoundsException e) {checkForComodif ication() ;throw new NoSuchElementException() ;

}

}hasNext屮关键的函数调用是size,获取集合的大小。 next方法屮关键的函数调用是get,获取第i个元素oGuava的实现返冋了一个被覆盖了size和get方法的AbstractList,巧妙的复用了由编译器生成的数组避免了新建列表和増加元素的开销。

空指针处理

当待拼接列表中可能包含空指针时我们用useForNul l将空指针替换为我们指定的字符串。它是通过返回一个覆盖了方法的Joiner实例来实现的。public Joiner useForNul1 (final String nullText) {chcckNotNull (nullTcxt) ;return new Joiner(this) {

©Override CharSequenee toString (©Nullable Object part) {return (part二二null) ? rmllText : Joiner, this. toString(part) ; }

©Override public Joiner useForNull (String nullText) {throw new UnsupportedOperationException("already specified useForNull") ;

}

©Override public Joiner skipNulls() {throw new UnsupportedOperationException ("already specified useForNull") ;

}

} 

}

首先是使用复制构造函数保留先前初始化吋候设置的分隔符然后覆盖了之前提到的toString方法。为了防止重复调用useForNull和skipNulls,还特意覆盖了这两个方法一旦调用就抛出运行时异常。为什么不能重复调用useForNull ?因为覆盖了toString方法而覆盖实现屮需要调用覆盖前的toStringo在不支持的操作中抛出UnsupportedOperat ionExcept ion是Guava的常见做法可以在第一时间纠正不科学的调用方式。skipNulls的实现就相对要复杂一些覆盖了原先全功能appendTo中使用if和while的优雅实现变成了2个wh i l e先后执行。第一个wh i l e找到第一个不为空指针的元素起到之前的if的功能笫二个while功能和之前的一致。

public Joiner skipNul1s() {return new Joincr(this) {

©Override public <A extends Appendable> A appendTo(A appendable, Iterator<?>parts)throws IOException {checkNotNull (appendable, 〃 appendable〃 ) ;chcckNotNull (parts, "parts") ; while (parts. hasNext () ) {

Object part = parts, next () ;if (part != null) {appendable, append(Joiner, this. toString(part) ) ;break;

}

}while (parts. hasNext () ) { Object part二parts.next () ; if (part != null)

{appendablc. appcnd (separator) ;appendable.append (Joiner, this. toString(part) ) ;

}

}return appendable;

}

©Override public Joiner useForNull (String nullText) {throw new UnsupportedOperationException("already specified skipNulls") ;

}

©Override public Mapjoiner withKeyValueSeparator(String kvs) { throw newUnsupportedOperationException(//can t use . skipNulls() with maps") ;

 

拼接键值对

Mapjoiner实现为Joiner的一个静态内部类它的构造函数和JoineT样也是私冇只能通过Joiner#wi thKeyValueSepeirdtor來生成实例。类似地 Mapjoincr也实现了appendTo方法和一系列的重载还用join方法对appendTo做了封装。Mapjoiner整个实现和Joiner大同小异,在实现中大量使用Joiner的toString方法来保证空指针保护彳亍为和初始化时的语义一致。

Mapjoiner也实现了一个useForMull方法这样的好处是在获取Mapjoiner之后再去设置空指针保护和获取Mapjoiner之前就设置空指针保护是等价的用户无需去关心顺序问题。

可抵御99%的攻击中国单域版cdn:9元/月7T防御 cloudsecre

官方网站:点击访问CDN客服QQ:123008公司名:贵州青辞赋文化传媒有限公司域名和IP被墙封了怎么办?用cloudsecre.com网站被攻击了怎么办?用cloudsecre.com问:黑客为什么要找网站来攻击?答:黑客需要找肉鸡。问:什么是肉鸡?答:被控的服务器和电脑主机就是肉鸡。问:肉鸡有什么作用?答:肉鸡的作用非常多,可以用来干违法的事情,通常的行为有:VPN拨号,流量P2P,攻击傀儡,...

A2Hosting三年付$1.99/月,庆祝18周年/WordPress共享主机最高优惠81%/100GB SSD空间/无限流量

A2Hosting主机,A2Hosting怎么样?A2Hosting是UK2集团下属公司,成立于2003年的老牌国外主机商,产品包括虚拟主机、VPS和独立服务器等,数据中心提供包括美国、新加坡softlayer和荷兰三个地区机房。A2Hosting在国外是一家非常大非常有名气的终合型主机商,拥有几百万的客户,非常值得信赖,国外主机论坛对它家的虚拟主机评价非常不错,当前,A2Hosting主机庆祝1...

GreenCloudVPS$20/年多国机房可选,1核@Ryzen 3950x/1GB内存/30GB NVMe/10Gbps端口月流量2TB

GreencloudVPS此次在四个机房都上线10Gbps大带宽VPS,并且全部采用AMD处理器,其中美国芝加哥机房采用Ryzen 3950x处理器,新加坡、荷兰阿姆斯特丹、美国杰克逊维尔机房采用Ryzen 3960x处理器,全部都是RAID-1 NVMe硬盘、DDR4 2666Mhz内存,GreenCloudVPS本次促销的便宜VPS最低仅需20美元/年,支持支付宝、银联和paypal。Gree...

java开发工具为你推荐
安装程序配置服务器失败安装用友u8的数据库最后说:“安装程序配置服务器失败。参考服务器错误日志和 C:WINDOWSsqlstp.log 了解更多信息。”暴风影音怎么截图暴风影音怎么截屏啊?照片转手绘照片转手绘用什么APP镜像文件是什么什么是镜像文件啊办公协同软件协同企业办公的软件有哪些?如何建立一个网站如何建立一个网站网易公开课怎么下载网易公开课的视频该如何下载?开机滚动条开机滚动条要很长时间怎么解决?安装迅雷看看播放器迅雷看看不能播放,说我尚未安装迅雷看看播放器什么是云平台谁能简单说一下什么是云平台啊?
深圳域名注册 域名注册使用godaddy 网游服务器租用 韩国vps 高防直连vps 主机屋 pw域名 z.com debian7 发包服务器 华为网络硬盘 有益网络 申请个人网站 炎黄盛世 hkg 135邮箱 阿里校园 双线机房 空间登入 百度云空间 更多