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

提速啦(24元/月)河南BGP云服务器活动 买一年送一年4核 4G 5M

提速啦的来历提速啦是 网站 本着“良心 便宜 稳定”的初衷 为小白用户避免被坑 由赣州王成璟网络科技有限公司旗下赣州提速啦网络科技有限公司运营 投资1000万人民币 在美国Cera 香港CTG 香港Cera 国内 杭州 宿迁 浙江 赣州 南昌 大连 辽宁 扬州 等地区建立数据中心 正规持有IDC ISP CDN 云牌照 公司。公司购买产品支持3天内退款 超过3天步退款政策。提速啦的市场定位提速啦主...

Cloudxtiny:£1.5/月,KVM-512MB/100GB/英国机房

Cloudxtiny是一家来自英国的主机商,提供VPS和独立服务器租用,在英国肯特自营数据中心,自己的硬件和网络(AS207059)。商家VPS主机基于KVM架构,开设在英国肯特机房,为了庆祝2021年欧洲杯决赛英格兰对意大利,商家为全场VPS主机提供50%的折扣直到7月31日,优惠后最低套餐每月1.5英镑起。我们对这场比赛有点偏见,但希望这是一场史诗般的决赛!下面列出几款主机套餐配置信息。CPU...

青云互联:洛杉矶CN2弹性云限时七折,Cera机房三网CN2gia回程,13.3元/月起

青云互联怎么样?青云互联是一家成立于2020年6月份的主机服务商,致力于为用户提供高性价比稳定快速的主机托管服务,目前提供有美国免费主机、香港主机、香港服务器、美国云服务器,让您的网站高速、稳定运行。目前,美国洛杉矶cn2弹性云限时七折,美国cera机房三网CN2gia回程 13.3元/月起,可选Windows/可自定义配置。点击进入:青云互联官网青云互联优惠码:七折优惠码:dVRKp2tP (续...

java开发工具为你推荐
淘宝收费淘宝卖东西收多少手续费人人时光机寻时光机歌词spgnux思普操作系统怎么样云播怎么看片云播影视怎么样?苹果5怎么越狱苹果5怎么越狱?qq怎么发邮件如何通过QQ发送邮件免费免费建站电脑上有真正免费的网站吗??网站优化方案几种常用的网站优化方法中国杀毒软件排行榜杀毒软件的最新排名?中国的排名?office2007简体中文版如何激活office2007 professional简体中文专业版啊?
域名交易 免费域名解析 精品网 好看的桌面背景大图 国内php空间 嘉洲服务器 电子邮件服务器 200g硬盘 免费吧 php空间购买 hktv linux使用教程 ca187 最漂亮的qq空间 贵阳电信测速 服务器论坛 atom处理器 114dns 免费个人网页 腾讯网盘 更多