java排成星_GitHub 标星 1.3k+,一款超赞的用于字符串处理的 Java 8 库

原标题:GitHub 标星 1.3k+,一款超赞的用于字符串处理的 Java 8 库

来自公众号: 沉默王二

很多初学编程的同学,经常给我吐槽,说:“二哥,你在敲代码的时候会不会有这样一种感觉,写着写着看不下去了,觉得自己写出来的代码就好像屎一样?”

这里我必须得说一句,初入“江湖”的时候,确实会觉得自己的代码写得很烂,但这么多年下来,这种感觉已经荡然无存了。

(吹嘛,我也会,哈哈)

那,怎么才能让写出来的代码不那么烂呢?

我的一个经验就是,“拿来主义”,尽量不去重复造轮子。使用那些已经被验证过,足够优质的开源库不仅能够让我们的代码变得优雅,还能够让我们在不断的使用过程当中,学习到编程的精髓。

洋务运动的时候,有一句很响亮的口号叫做,“师夷长技以制夷”。先去用,再去学,自然而然就会变得牛逼。同学们,你们说,是不是这个理?

我今天推荐的这款开源库,名字叫做 strman-java,GitHub 上标星 1.3k,一款超赞的字符串处理工具库,基于 Java 8,语法非常简洁。

接下来,我们来看看怎么用。

Maven 项目只需要在 pom.xml 文件中添加以下依赖即可。

< dependency>

< groupId>com.shekhargulati groupId>

< artifactId>strman artifactId>

< version>0.4.0 version>

dependency>

好了,可以肆无忌惮地调用 strman-java 的 API 了。我会在介绍的时候插入一些源码的介绍,方便同学们更深一步的学习,尽量做到“知其然知其所以然”。

87b5e9ba27e4d03ec8690b785b0e103a.png

01、append

把可变字符串参数添加到指定的字符串尾部。

Strman.append( "沉", "默", "王", "二");

结果如下所示:

沉默王二

append 对应的方法是 prepend,把可变字符串参数前置到指定的字符串前面,使用方法如下。

Strman.prepend( "沉", "默", "王", "二");

结果如下所示:

默王二沉

02、appendArray

把字符串数组添加到指定的字符串尾部。

String [] strs = { "默", "王", "二"};

Strman.appendArray( "沉",strs);

结果如下所示:

沉默王二

append 内部其实调用的 appendArray,来看一下源码:

publicstaticString append( finalString value, finalString... appends){

returnappendArray(value, appends);

}

当使用可变参数的时候,实际上是先创建了一个数组,该数组的大小就是可变参数的个数,然后将参数放入数组当中,再将数组传递给被调用的方法。

通过观察反编译后的字节码,就能看得到。

Strman.append( "沉", "默", "王", "二");

实际等同于:

Strman.append( "沉", newString[]{ "默", "王", "二"});

再来看一下 appendArray 方法的源码:

publicstaticString appendArray( finalString value, finalString[] appends){

StringJoiner joiner = newStringJoiner( "");

for(String append : appends) {

joiner.add(append);

}

returnvalue + joiner.toString;

}

内部用的 StringJoiner,Java 8 时新增的一个类。构造方法有两种。

第一种,指定分隔符:

publicStringJoiner(CharSequence delimiter){

this(delimiter, "", "");

}

第二种,指定分隔符、前缀、后缀:

publicStringJoiner(CharSequence delimiter,

CharSequence prefix,

CharSequence suffix) {

this.prefix = prefix.toString;

this.delimiter = delimiter.toString;

this.suffix = suffix.toString;

}

虽然也可以在 StringBuilder 类的帮助下在每个字符串之后附加分隔符,但 StringJoiner 提供了更简单的方法来实现,无需编写大量的代码。

03、at

获取指定索引处上的字符。

Strman.at( "沉默王二", 0);

Strman.at( "沉默王二", - 1);

Strman.at( "沉默王二", 4);

结果如下所示:

Optional[沉]

Optional[二]

Optional.empty

也就是说,at 可以处理 -(length-1) 到 (length-1) 之内的索引(当索引为负数的时候将从末尾开始查找),如果超出这个范围,将会返回 Optional.empty ,避免发生空指针。

来看一下源码:

publicstaticOptional at( finalString value, intindex){

if(isNullOrEmpty(value)) {

returnOptional.empty;

}

intlength = value.length;

if(index < 0) {

index = length + index;

}

return(index < length && index >= 0) ? Optional.of(String.valueOf(value.charAt(index))) : Optional.empty;

}

本质上,是通过 String 类的 charAt 方法查找的,但包裹了一层 Optional,就巧妙地躲开了烦人的空指针。

Optional 是 Java 8 时新增的一个类,该类提供了一种用于表示可选值而非空引用的类级别解决方案。

6c15997d909ac60ef9807ac310264699.png

04、between

按照指定起始字符和截止字符来返回一个字符串数组。

String [] results = Strman.between( "[沉默王二][一枚有趣的程序员]", "[", "]");

System.out.println(Arrays.toString(results));

结果如下所示:

[沉默王二, 一枚有趣的程序员]

来看一下源码:

publicstaticString[] between( finalString value, finalString start, finalString end) {

String[] parts = value.split(end);

returnArrays.stream(parts).map(subPart -> subPart.substring(subPart.indexOf(start) + start.length))

.toArray(String[]:: new);

}

java.util.Arrays 类是为数组而生的专用工具类,基本上常见的对数组的操作,Arrays 类都考虑到了, stream 方法可以将数组转换成流:

String[] intro = newString[] { "沉", "默", "王", "二"};

Arrays.stream(intro);

Java 8 新增的 Stream 流在很大程度上提高了开发人员在操作集合(Collection)时的生产力。要想操作流,首先需要有一个数据源,可以是数组或者集合。每次操作都会返回一个新的流对象,方便进行链式操作,但原有的流对象会保持不变。

map 方法可以把一个流中的元素转化成一个新流中的元素,它可以接收一个 Lambda 表达式作为参数。Lambda 表达式描述了一个代码块(或者叫匿名方法),可以将其作为参数传递给构造方法或者普通方法以便后续执行。

考虑下面这段代码:

-> System.out.println( "沉默王二")

来从左到右解释一下, 为 Lambda 表达式的参数列表(本例中没有参数), -> 标识这串代码为 Lambda 表达式(也就是说,看到 -> 就知道这是 Lambda), System.out.println("沉默王二") 为要执行的代码,即将“沉默王二”打印到标准输出流。

toArray 方法可以将流转换成数组,你可能比较好奇的是 String[]::new ,它是什么东东呢?来看一下 toArray 方法的源码。

A[] toArray(IntFunction generator);

也就是说 String[]::new 是一个 IntFunction,一个可以产生所需的新数组的函数,可以通过反编译字节码看看它到底是什么:

String[] strArray = (String[])list.stream .toArray( (x$ 0)->{

returnnewString[x$ 0];

});

也就是相当于返回了一个指定长度的字符串数组。

05、chars

返回组成字符串的单个字符的数组。

String [] results = Strman.chars( "沉默王二");

System.out.println(Arrays.toString(results));

结果如下所示:

[沉, 默, 王, 二]

来看一下源码:

publicstaticString[] chars( finalString value) {

returnvalue.split( "");

}

内部是通过 String 类的 split 方法实现的。

06、charsCount

统计字符串中每个字符出现的次数。

Map map = Strman.charsCount( "沉默王二的妹妹叫沉默王三");

System.out.println(map);

结果如下所示:

{的=1, 默=2, 三=1, 妹=2, 沉=2, 叫=1, 王=2, 二=1}

是不是瞬间觉得这个方法有意思多了,一步到位,统计出字符串中各个字符出现的次数,来看一下源码吧。

publicstaticMap charsCount(String input){

returninput.chars.mapToObj(c -> ( char) c).collect(groupingBy(identity, counting));

}

String 类的 chars 方法是 Java 9 新增的,它返回一个针对基本类型 int 的流:IntStream。

mapToObj 方法主要是将 Stream 中的元素进行装箱操作, 转换成一个引用类型的值, 它接收一个 IntFunction 接口, 它是一个 int -> R 的函数接口。

collect 方法可以把流转成集合 Map。

07、collapseWhitespace

用单个空格替换掉多个连续的空格。

Strman.collapseWhitespace( "沉默王二 一枚有趣的程序员");

结果如下所示:

Strman.collapseWhitespace( "沉默王二 一枚有趣的程序员")

来看一下源码:

publicstaticString collapseWhitespace( finalString value){

returnvalue.trim.replaceAll( "ss+", " ");

}

内部先用 trim 方法去掉两侧的空格,然后再用正则表达式将多个连续的空格替换成单个空格。

08、contains

验证指定的字符串是否包含某个字符串。

System.out.println(Strman.contains( "沉默王二", "沉"));

System.out.println(Strman.contains( "Abbc", "a", false));

结果如下所示:

true

true

第三个参数 caseSensitive 是可选项,如果为 false 则表明不区分大小写。

来看一下源码:

publicstaticbooleancontains( finalString value, finalString needle, finalbooleancaseSensitive){

if(caseSensitive) {

returnvalue.contains(needle);

}

returnvalue.toLowerCase.contains(needle.toLowerCase);

}

内部通过 String 类的 contains 方法实现,如果不区分大小写,则先调用 toLowerCase 方法转成小写。

09、containsAny

验证指定的字符串是否包含字符串数组中任意一个字符串,或更多。

System.out.println(Strman.containsAny( "沉默王二", newString [] { "沉", "三"}));

System.out.println(Strman.containsAny( "沉默王二", newString [] { "沉默", "三"}));

System.out.println(Strman.containsAny( "沉默王二", newString [] { "不", "三"}));

结果如下所示:

true

true

false

来看一下源码:

publicstaticbooleancontainsAny( finalString value, finalString[] needles, finalbooleancaseSensitive){

returnArrays.stream(needles).anyMatch(needle -> contains(value, needle, caseSensitive));

}

Stream 类提供了三个方法可供进行元素匹配,它们分别是:

anyMatch ,只要有一个元素匹配传入的条件,就返回 true。

allMatch ,只有有一个元素不匹配传入的条件,就返回 false;如果全部匹配,则返回 true。

noneMatch ,只要有一个元素匹配传入的条件,就返回 false;如果全部匹配,则返回 true。

10、endsWith

验证字符串是否以某个字符串结尾。

System.out.println(Strman.endsWith( "沉默王二", "二"));

System.out.println(Strman.endsWith( "Abbc", "A", false));

结果如下所示:

true

false

来看一下源码:

publicstaticbooleanendsWith( finalString value, finalString search, finalintposition,

finalbooleancaseSensitive) {

intremainingLength = position - search.length;

if(caseSensitive) {

returnvalue.indexOf(search, remainingLength) > - 1;

}

returnvalue.toLowerCase.indexOf(search.toLowerCase, remainingLength) > - 1;

}

内部通过 String 类的 indexOf 方法实现。

11、ensureLeft

确保字符串以某个字符串开头,如果该字符串没有以指定的字符串开头,则追加上去。

System.out.println(Strman.ensureLeft( "沉默王二", "沉"));

System.out.println(Strman.ensureLeft( "默王二", "沉"));

结果如下所示:

沉默王二

沉默王二

来看一下源码:

publicstaticString ensureLeft( finalString value, finalString prefix, finalbooleancaseSensitive){

if(caseSensitive) {

returnvalue.startsWith(prefix) ? value : prefix + value;

}

String _value = value.toLowerCase;

String _prefix = prefix.toLowerCase;

return_value.startsWith(_prefix) ? value : prefix + value;

}

内部通过 String 类的 startsWith 方法先进行判断,如果结果为 false,则通过“+”操作符进行连接。

ensureLeft 对应的还有 ensureRight,同理,这里不再赘述。

12、base64Encode

把字符串进行 base64 编码。

Strman.base64Encode( "沉默王二");

结果如下所示:

5rKJ6buY546L5LqM

Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法。来看一下源码:

publicstaticString base64Encode( finalString value){

returnBase64.getEncoder.encodeToString(value.getBytes(StandardCharsets.UTF_8));

}

内部是通过 Base64 类实现的,Java 8 新增的一个类。

base64Encode 对应的解码方法是 base64Decode,使用方法如下所示:

Strman.base64Decode( "5rKJ6buY546L5LqM")

如果不可解码的会,会抛出 IllegalArgumentException 异常。

Exception inthread "main"java.lang. IllegalArgumentException:Last unit does nothave enough valid bits

at java.base/java.util.Base64$Decoder.decode 0(Base64. java:763)

at java.base/java.util.Base64$Decoder.decode(Base64. java:535)

at java.base/java.util.Base64$Decoder.decode(Base64. java:558)

at strman.Strman.base64Decode(Strman. java:328)

at com.itwanger.strman.Demo.main(Demo. java:58)

13、binEncode

把字符串转成二进制的 Unicode (16 位)。

Strman.binEncode( "沉默王二");

结果如下所示:

0110110010001001100111101101100001110011100010110100111010001100

binEncode 对应的方法是 binDecode,把二进制的 Unicode 转成字符串,使用方法如下所示:

Strman.binDecode( "0110110010001001100111101101100001110011100010110100111010001100");

14、first

返回字符串的前 N 个字符。

System.out.println(Strman.first( "沉默王二", 0));

System.out.println(Strman.first( "沉默王二", 1));

System.out.println(Strman.first( "沉默王二", 2));

结果如下所示:

Optional[]

Optional[沉]

Optional[沉默]

如果 N 为负数的话,将会抛出 StringIndexOutOfBoundsException 异常:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin0, end-1, length4

atjava.base/java.lang.String.checkBoundsBeginEnd(String.java: 3319)

atjava.base/java.lang.String.substring(String.java: 1874)

atstrman.Strman.lambda$ first$ 9(Strman.java: 414)

atjava.base/java.util.Optional.map(Optional.java: 265)

atstrman.Strman.first(Strman.java: 414)

atcom.itwanger.strman.Demo.main(Demo.java: 68)

针对 N 为负数的情况,我觉得没有之前的 at 方法处理的巧妙。

来看一下源码:

publicstaticOptional first( finalString value, finalintn){

returnOptional.ofNullable(value).filter(v -> !v.isEmpty).map(v -> v.substring( 0, n));

}

内部是通过 String 类的 substring 方法实现的,不过没有针对 n 小于 0 的情况做处理。

ofNullable 方法可以创建一个即可空又可非空的 Optional 对象。

filter 方法的参数类型为 Predicate(Java 8 新增的一个函数式接口),也就是说可以将一个 Lambda 表达式传递给该方法作为条件,如果表达式的结果为 false,则返回一个 EMPTY 的 Optional 对象,否则返回过滤后的 Optional 对象。

map 方法可以按照一定的规则将原有 Optional 对象转换为一个新的 Optional 对象,原有的 Optional 对象不会更改。

first 对应的的是 last 方法,返回字符串的后 N 个字符。

15、head

返回字符串的第一个字符。

Strman.head( "沉默王二");

结果如下所示:

Optional[沉]

来看一下源码:

publicstaticOptional head( finalString value){

returnfirst(value, 1);

}

内部是通过调用 first 方法实现的,只不过 N 为 1。

16、unequal

检查两个字符串是否不等。

Strman.unequal( "沉默王二", "沉默王三");

结果如下所示:

true

来看一下源码:

publicstaticbooleanunequal( finalString first, finalString second){

return!Objects.equals(first, second);

}

内部是通过 Objects.equals 方法进行判断的,由于 String 类重写了 equals 方法,也就是说,实际上还是通过 String 类的 equals 方法进行判断的。

17、insert

把字符串插入到指定索引处。

Strman.insert( "沉默二", "王", 2);

结果如下所示:

沉默王二

来看一下源码:

publicstaticString insert( finalString value, finalString substr, finalintindex){

if(index > value.length) {

returnvalue;

}

returnappend(value.substring( 0, index), substr, value.substring(index));

}

如果索引超出字符串长度,直接返回原字符串;否则调用 append 方法将指定字符串插入到对应索引处。

18、repeat

对字符串重复指定次数。

Strman.repeat( "沉默王二", 3);

结果如下所示:

沉默王二沉默王二沉默王二

来看一下源码:

publicstaticString repeat( finalString value, finalintmultiplier){

returnStream.generate( -> value).limit(multiplier).collect(joining);

}

Stream.generate 生成的 Stream,默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。

collect(joining) 可以将流转成字符串。

19、leftPad

返回给定长度的新字符串,以便填充字符串的开头。

Strman.leftPad( "王二", "沉默", 6);

结果如下所示:

沉默沉默沉默沉默王二

来看一下源码:

publicstaticString leftPad( finalString value, finalString pad, finalintlength){

if(value.length > length) {

returnvalue;

}

returnappend(repeat(pad, length - value.length), value);

}

内部会先调用 repeat 方法进行补位,然后再调用 append 方法拼接。

leftPad 方法对应的是 rightPad,填充字符串的末尾。

19)removeEmptyStrings,从字符串数组中移除空字符串。

String [] results = Strman.removeEmptyStrings( newString[]{ "沉", " ", " ", "默王二"});

System.out.println(Arrays.toString(results));

结果如下所示:

[沉, 默王二]

来看一下源码:

publicstaticString[] removeEmptyStrings(String[] strings) {

if(Objects.isNull(strings)) {

thrownewIllegalArgumentException( "Input array should not be null");

}

returnArrays.stream(strings).filter(str -> str != null&& !str.trim.isEmpty).toArray(String[]:: new);

}

通过 Stream 的 filter 方法过滤掉了空格。

20、reverse

反转字符串。

Strman.reverse( "沉默王二");

结果如下所示:

二王默沉

来看一下源码:

publicstaticString reverse( finalString value){

returnnewStringBuilder(value).reverse.toString;

}

内部是通过 StringBuilder 类的 reverse 方法进行反转的。

21、safeTruncate

对字符串进行截断,但不会破坏单词的完整性。

Strman.safeTruncate( "Java is the best", 13, "...");

结果如下所示:

Java is...

来看一下源码:

publicstaticString safeTruncate( finalString value, finalintlength, finalString filler){

if(length == 0) {

return"";

}

if(length >= value.length) {

returnvalue;

}

String[] words = words(value);

StringJoiner result = newStringJoiner( " ");

intspaceCount = 0;

for(String word : words) {

if(result.length + word.length + filler.length + spaceCount > length) {

break;

} else{

result.add(word);

spaceCount++;

}

}

returnappend(result.toString, filler);

}

先调用 words 方法对字符串进行单词分割,然后按照长度进行截断,最后调用 append 方法填充上补位符。

safeTruncate 对应的是 truncate,可能会破坏单词的完整性,使用方法如下所示:

Strman.truncate( "Java is the best", 13, "...")

结果如下所示:

Java isth...

来看一下源码:

publicstaticString truncate( finalString value, finalintlength, finalString filler){

if(length == 0) {

return"";

}

if(length >= value.length) {

returnvalue;

}

returnappend(value.substring( 0, length - filler.length), filler);

}

就是单纯的切割和补位,没有对单词进行保护。

22、shuffle

对字符串重新洗牌。

Strman.shuffle( "沉默王二");

结果如下所示:

王默二沉

来看一下源码:

publicstaticString shuffle( finalString value){

String[] chars = chars(value);

Random random = newRandom;

for( inti = 0; i < chars.length; i++) {

intr = random.nextInt(chars.length);

String tmp = chars[i];

chars[i] = chars[r];

chars[r] = tmp;

}

returnArrays.stream(chars).collect(joining);

}

调用 chars 方法把字符串拆分为字符串数组,然后遍历对其重排,最后通过 Stream 转成新的字符串。

23、其他方法

Strman 中还有很多其他巧妙的字符串处理方法,比如说把字符串按照指定的前后缀进行包裹 surround 等等,同学们可以参考 Strman 的官方文档进行学习:

https://github.com/shekhargulati/strman-java/wiki返回搜狐,查看更多

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值