拼接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之前就设置空指针保护是等价的用户无需去关心顺序问题。

DogYun香港BGP月付14.4元主机简单测试

前些天赵容分享过DogYun(狗云)香港BGP线路AMD 5950X经典低价云服务器的信息(点击查看),刚好账户还有点余额够开个最低配,所以手贱尝试下,这些贴上简单测试信息,方便大家参考。官方网站:www.dogyun.com主机配置我搞的是最低款优惠后14.4元/月的,配置单核,512MB内存,10GB硬盘,300GB/50Mbps月流量。基本信息DogYun的VPS主机管理集成在会员中心,包括...

2022年最新PHP短网址生成系统/短链接生成系统/URL缩短器系统源码

全新PHP短网址系统URL缩短器平台,它使您可以轻松地缩短链接,根据受众群体的位置或平台来定位受众,并为缩短的链接提供分析见解。系统使用了Laravel框架编写,前后台双语言使用,可以设置多域名,还可以开设套餐等诸多功能,值得使用。链接: https://pan.baidu.com/s/1ti6XqJ22tp1ULTJw7kYHog?pwd=sarg 提取码: sarg文件解压密码 www.wn7...

EtherNetservers年付仅10美元,美国洛杉矶VPS/1核512M内存10GB硬盘1Gpbs端口月流量500GB/2个IP

EtherNetservers是一家成立于2013年的英国主机商,提供基于OpenVZ和KVM架构的VPS,数据中心包括美国洛杉矶、新泽西和杰克逊维尔,商家支持使用PayPal、支付宝等付款方式,提供 60 天退款保证,这在IDC行业来说很少见,也可见商家对自家产品很有信心。有需要便宜VPS、多IP VPS的朋友可以关注一下。优惠码SUMMER-VPS-15 (终身 15% 的折扣)SUMMER-...

java开发工具为你推荐
淘宝客推广淘宝客推广有用吗?支付宝查询余额怎么查询支付宝里的余额如何建立一个网站怎样能创建一个网站硬盘人克隆一个人需要多少人多长时间啊开机滚动条电脑开机有滚动条的画面怎么点亮qq空间图标怎么点亮QQ空间的图标保护气球气球保护液可以用什么来代替?电子商务网站模板我想开发一个电子商务网站,但是想加入自己设计的模板,可以吗?网站地图制作网站地图 怎么制作?域名库域名赎回期过了多长时间可以注册
虚拟主机管理系统 美国主机评测 rackspace godaddy支付宝 发包服务器 免费全能空间 cpanel空间 怎样建立邮箱 北京双线 cdn加速原理 isp服务商 免费申请网站 免费智能解析 ftp免费空间 万网空间购买 创建邮箱 网通服务器 下载速度测试 域名转入 云服务是什么意思 更多