欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java開(kāi)發(fā)使用StringUtils.split避坑詳解

 更新時(shí)間:2022年11月04日 17:03:37   作者:程序猿阿朗  
這篇文章主要為大家介紹了java開(kāi)發(fā)使用StringUtils.split避坑詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

在日常的 Java 開(kāi)發(fā)中,由于 JDK 未能提供足夠的常用的操作類(lèi)庫(kù),通常我們會(huì)引入 Apache Commons Lang 工具庫(kù)或者 Google Guava 工具庫(kù)簡(jiǎn)化開(kāi)發(fā)過(guò)程。兩個(gè)類(lèi)庫(kù)都為 java.lang API 提供了很多實(shí)用工具,比如經(jīng)常使用的字符串操作,基本數(shù)值操作、時(shí)間操作、對(duì)象反射以及并發(fā)操作等。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

但是,最近在使用 Apache Commons Lang 工具庫(kù)時(shí)踩了一個(gè)坑,導(dǎo)致程序出現(xiàn)了意料之外的結(jié)果。

StringUtils.split 的坑

也是因?yàn)椴攘诉@個(gè)坑,索性寫(xiě)下一篇文章好好介紹下 Apache Commons Lang 工具庫(kù)中字符串操作相關(guān) API。

先說(shuō)坑是什么,我們都知道 String 類(lèi)中到的 split 方法可以分割字符串,比如字符串 aabbccdd 根據(jù) bc 分割的結(jié)果應(yīng)該是 aab 和 cdd 才對(duì),這樣的結(jié)果也很容易驗(yàn)證。

String str = "aabbccdd";
for (String s : str.split("bc")) {
    System.out.println(s);
}
// 結(jié)果
aab
cdd

可能是因?yàn)?String 類(lèi)中的 split 方法的影響,我一直以為 StringUtils.split 的效果應(yīng)該相同,但其實(shí)完全不同,可以試著分析下面的三個(gè)方法輸出結(jié)果是什么,StringUtils 是 Commons Lang 類(lèi)庫(kù)中的字符串工具類(lèi)。

 public static void testA() {
    String str = "aabbccdd";
    String[] resultArray = StringUtils.split(str, "bc");
    for (String s : resultArray) {
        System.out.println(s);
    }
}

我對(duì)上面 testA 方法的預(yù)期是 aab 和 cdd ,但是實(shí)際上這個(gè)方法的運(yùn)行結(jié)果是:

// testA 輸出
aa
dd

可以看到 b 和 c 字母都不見(jiàn)了,只剩下了 a 和 b,這是已經(jīng)發(fā)現(xiàn)問(wèn)題了,查看源碼后發(fā)現(xiàn) StringUtils.split 方法其實(shí)是按字符進(jìn)行操作的,不會(huì)把分割字符串作為一個(gè)整體來(lái)看,返回的結(jié)果中不也會(huì)包含用于分割的字符。

驗(yàn)證代碼:

public static void testB() {
    String str = "abc";
    String[] resultArray = StringUtils.split(str, "ac");
    for (String s : resultArray) {
        System.out.println(s);
    }
}
// testB 輸出
b
public static void testC() {
    String str = "abcd";
    String[] resultArray = StringUtils.split(str, "ac");
    for (String s : resultArray) {
        System.out.println(s);
    }
}
// testC 輸出
b
d

輸出結(jié)果和預(yù)期的一致了。

StringUtils.split 源碼分析

點(diǎn)開(kāi)源碼一眼看下去,發(fā)現(xiàn)在方法注釋中就已經(jīng)進(jìn)行提示了:返回的字符串?dāng)?shù)組中不包含分隔符

The separator is not included in the returned String array. Adjacent separators are treated as one separator. For more control over the split use the StrTokenizer class....

繼續(xù)追蹤源碼,可以看到最終 split 分割字符串時(shí)入?yún)⒂兴膫€(gè)。

private static String[] splitWorker(
final String str, // 原字符串 
final String separatorChars,  // 分隔符
final int max,  // 分割后返回前多少個(gè)結(jié)果,-1 為所有
final boolean preserveAllTokens // 暫不關(guān)注
) {
}

根據(jù)分隔符的不同又分了三種情況。

1. 分隔符為 null

final int len = str.length();
if (len == 0) {
    return ArrayUtils.EMPTY_STRING_ARRAY;
}
final List<String> list = new ArrayList<>();
int sizePlus1 = 1;
int i = 0;
int start = 0;
boolean match = false;
boolean lastMatch = false;
if (separatorChars == null) {
    // Null separator means use whitespace
    while (i < len) {
        if (Character.isWhitespace(str.charAt(i))) { 
            if (match || preserveAllTokens) {
                lastMatch = true;
                if (sizePlus1++ == max) {
                    i = len;
                    lastMatch = false;
                }
                list.add(str.substring(start, i));
                match = false;
            }
            start = ++i;
            continue;
        }
        lastMatch = false;
        match = true;
        i++;
    }
}
// ...
if (match || preserveAllTokens && lastMatch) {
            list.add(str.substring(start, i));
}

可以看到如果分隔符為 null ,是按照空白字符 Character.isWhitespace() 分割字符串的。分割的算法邏輯為:

a. 用于截取的開(kāi)始下標(biāo)置為 0 ,逐字符讀取字符串。

b. 碰到分割的目標(biāo)字符,把截取的開(kāi)始下標(biāo)到當(dāng)前字符之前的字符串截取出來(lái)。

c. 然后用于截取的開(kāi)始下標(biāo)置為下一個(gè)字符,等到下一次使用。

d. 繼續(xù)逐字符讀取字符串、

2. 分隔符為單個(gè)字符

邏輯同上,只是判斷邏輯 Character.isWhitespace() 變?yōu)榱酥付ㄗ址袛唷?/p>

// Optimise 1 character case
final char sep = separatorChars.charAt(0);
while (i < len) {
    if (str.charAt(i) == sep) { // 直接比較
      ...

3. 分隔符為字符串

總計(jì)邏輯同上,只是判斷邏輯變?yōu)榘袛唷?/p>

 // standard case
while (i < len) {
    if (separatorChars.indexOf(str.charAt(i)) >= 0) { // 包含判斷
        if (match || preserveAllTokens) {

如何解決?

1. 使用 splitByWholeSeparator 方法。

我們想要的是按整個(gè)字符串分割,StringUtils 工具類(lèi)中已經(jīng)存在具體的實(shí)現(xiàn)了,使用 splitByWholeSeparator 方法。

String str = "aabbccdd";
String[] resultArray = StringUtils.splitByWholeSeparator(str, "bc");
for (String s : resultArray) {
    System.out.println(s);
}
// 輸出
aab
cdd

2. 使用 Google Guava 工具庫(kù)

關(guān)于 Guava 工具庫(kù)的使用,之前也寫(xiě)過(guò)一篇文章,可以參考:Guava - 拯救垃圾代碼

String str = "aabbccdd";
Iterable<String> iterable = Splitter.on("bc")
    .omitEmptyStrings() // 忽略空值
    .trimResults() // 過(guò)濾結(jié)果中的空白
    .split(str);
iterable.forEach(System.out::println);
// 輸出
aab
cdd

3. JDK String.split 方法

使用 String 中的 split 方法可以實(shí)現(xiàn)想要效果。

String str = "aabbccdd";
String[] res = str.split("bc");
for (String re : res) {
    System.out.println(re);
}
// 輸出
aab
cdd

但是 String 的 split 方法也有一些坑,比如下面的輸出結(jié)果。

String str = ",a,,b,";
String[] splitArr = str.split(",");
Arrays.stream(splitArr).forEach(System.out::println);
// 輸出
a
b

開(kāi)頭的逗號(hào),前出現(xiàn)了空格,末尾的逗號(hào),后卻沒(méi)有空格。

一如既往,文章中代碼存放在 Github.com/niumoo/javaNotes.

以上就是java開(kāi)發(fā)使用StringUtils.split避坑詳解的詳細(xì)內(nèi)容,更多關(guān)于java開(kāi)發(fā)StringUtils.split避坑的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于SpringBoot實(shí)現(xiàn)動(dòng)態(tài)配置數(shù)據(jù)庫(kù)的加載

    基于SpringBoot實(shí)現(xiàn)動(dòng)態(tài)配置數(shù)據(jù)庫(kù)的加載

    這篇文章主要介紹了Spring?Boot?如何動(dòng)態(tài)配置數(shù)據(jù)庫(kù)的加載,現(xiàn)項(xiàng)目有一個(gè)需求,期望通過(guò)在application.yml配置文件中設(shè)置一個(gè)開(kāi)關(guān),來(lái)決定是否加載數(shù)據(jù)庫(kù),文中通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • SpringBoot中的RabbitMQ用法詳解

    SpringBoot中的RabbitMQ用法詳解

    RabbitMQ是一個(gè)開(kāi)源的消息隊(duì)列系統(tǒng),它通過(guò)AMQP(高級(jí)消息隊(duì)列協(xié)議)來(lái)實(shí)現(xiàn)消息的傳遞,SpringBoot是目前非常流行的Java開(kāi)發(fā)框架,它提供了很多便利性的功能,其中就包括對(duì)RabbitMQ的支持,在本文中,我們將介紹如何在SpringBoot中使用RabbitMQ
    2023-07-07
  • java 基礎(chǔ)之final、finally和finalize的區(qū)別

    java 基礎(chǔ)之final、finally和finalize的區(qū)別

    這篇文章主要介紹了java 基礎(chǔ)之final、finally和finalize的區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • 鑒權(quán)認(rèn)證+aop+注解+過(guò)濾feign請(qǐng)求的實(shí)例

    鑒權(quán)認(rèn)證+aop+注解+過(guò)濾feign請(qǐng)求的實(shí)例

    這篇文章主要介紹了鑒權(quán)認(rèn)證+aop+注解+過(guò)濾feign請(qǐng)求的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java用棧實(shí)現(xiàn)綜合計(jì)算器

    Java用棧實(shí)現(xiàn)綜合計(jì)算器

    棧(stack)又名堆棧,它是一種運(yùn)算受限的線性表,下面看一下如何在Java中,利用數(shù)組實(shí)現(xiàn)模擬一個(gè)棧,感興趣的朋友跟隨小編一起看看吧
    2022-06-06
  • JavaApi實(shí)現(xiàn)更新刪除及讀取節(jié)點(diǎn)

    JavaApi實(shí)現(xiàn)更新刪除及讀取節(jié)點(diǎn)

    這篇文章主要介紹了JavaApi實(shí)現(xiàn)更新刪除及讀取節(jié)點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • jetty運(yùn)行時(shí)無(wú)法保存文件的解決方法

    jetty運(yùn)行時(shí)無(wú)法保存文件的解決方法

    這篇文章主要為大家詳細(xì)介紹了jetty運(yùn)行時(shí)無(wú)法保存文件的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 在SpringBoot項(xiàng)目中使用JetCache緩存的詳細(xì)教程

    在SpringBoot項(xiàng)目中使用JetCache緩存的詳細(xì)教程

    Spring Boot是一個(gè)非常流行的Java開(kāi)發(fā)框架,JetCache是一個(gè)基于注解的高性能緩存框架,本文將介紹如何在Spring Boot項(xiàng)目中使用JetCache緩存,并提供一個(gè)詳細(xì)案例來(lái)說(shuō)明如何配置和使用JetCache,需要的朋友可以參考下
    2024-06-06
  • 基于指針pointers和引用references的區(qū)別分析

    基于指針pointers和引用references的區(qū)別分析

    本篇文章介紹了,基于指針pointers和引用references的區(qū)別分析。需要的朋友參考下
    2013-05-05
  • SpringMVC文件上傳及查看的示例代碼

    SpringMVC文件上傳及查看的示例代碼

    本篇文章主要介紹了SpringMVC文件上傳及查看的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01

最新評(píng)論