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

BuyVM老牌商家新增迈阿密机房 不限流量 月付2美元

我们很多老用户对于BuyVM商家还是相当熟悉的,也有翻看BuyVM相关的文章可以追溯到2014年的时候有介绍过,不过那时候介绍这个商家并不是很多,主要是因为这个商家很是刁钻。比如我们注册账户的信息是否完整,以及我们使用是否规范,甚至有其他各种问题导致我们是不能购买他们家机器的。以前你嚣张是很多人没有办法购买到其他商家的机器,那时候其他商家的机器不多。而如今,我们可选的商家比较多,你再也嚣张不起来。...

2021年恒创科技618活动:香港/美国服务器/云服务器/高防全场3折抢购

2021年恒创科技618活动香港美国服务器/云服务器/高防全场3折抢购,老客户续费送时长,每日限量秒杀。云服务器每款限量抢购,香港美国独服/高防每款限量5台/天,香港节点是CN2线路还不错。福利一:爆品秒杀 超低价秒杀,秒完即止;福利二:云服务器 火爆机型 3折疯抢;福利三:物理服务器 爆款直降 800元/月起;福利四:DDOS防护 超强防御仅 1750元/月。点击进入:2021年恒创科技618活...

御云(RoyalYun):香港CN2 GIA VPS仅7.9元每月起,美国vps仅8.9/月,续费同价,可叠加优惠

御云怎么样?炎炎暑期即将来临,御云(royalyun)香港、美国服务器开启大特惠模式。御云是新成立的云服务提供商,主要提供香港、美国的云服务器,不久将开启虚拟主机业务。我们的香港和美国主机采用CN2 GIA线路。目前,香港cn2 gia vps仅7.9元每月起,美国vps仅8.9/月,续费同价,可叠加优惠,香港云服务器国内延迟一般在50ms左右,是搭建网站的最佳选择,但是请不要用于违法用途。点击进...

java开发工具为你推荐
苏州商标注册苏州如何申请商标注册?人人时光机五月天的专辑都有哪些?真正免费的网络电话有真正的免费的网络电话吗 ?打开网页出现错误网页出现错误怎么解决?要最简单的那种照片转手绘有什么软件可以把相片变成手绘的,不是美图秀秀里面的网店推广网站可以介绍几个可以做店铺推广的网站吗?童之磊华硕的四核平板电脑,怎么样?滚动代码来回滚动代码腾讯文章怎样才能在手机腾讯网上发表文章?畅想中国用“心系祖国情,畅想中国梦”为题目的800字作文
汉邦高科域名申请 hawkhost 鲨鱼机 php主机 缓存服务器 realvnc 京东商城0元抢购 100x100头像 宁波服务器 河南移动网 新世界服务器 海外空间 中国联通宽带测试 免费赚q币 九零网络 阿里云宕机故障 赵蓉 好看的空间留言代码 电脑主机内部结构 运维堡垒主机 更多