web-dev-qa-db-ja.com

なぜJava 8 splitは結果配列の開始時に空の文字列を削除することがありますか?

Before Java 8空の文字列で分割する場合

String[] tokens = "abc".split("");

分割メカニズムは、|でマークされた場所で分割します

|a|b|c|

これは、各文字の前後に空のスペース""が存在するためです。結果として、最初にこの配列を生成します

["", "a", "b", "c", ""]

後で 末尾の空の文字列を削除limit引数に明示的に負の値を指定しなかったため)、最終的に戻ります

["", "a", "b", "c"]

In Java 8分割メカニズムは変更されたようです。

"abc".split("")

["a", "b", "c"]の代わりに["", "a", "b", "c"]配列を取得するため、開始時に空の文字列も削除されるように見えます。しかし、この理論は失敗します

"abc".split("a")

開始時に空の文字列を含む配列を返します["", "bc"]

誰かがここで何が起こっているのか、Java 8?

107
Pshemo

String.splitPattern.splitを呼び出す)の動作は、Java 7とJava 8。

ドキュメンテーション

Java 7Java 8Pattern.splitのドキュメントを比較すると、次の句が追加されていることがわかります。

入力シーケンスの先頭に正の幅の一致がある場合、結果の配列の先頭に空の先行部分文字列が含まれます。ただし、先頭の幅がゼロの一致では、このような空の先行部分文字列は生成されません。

同じ句が Java 7 と比較して、 Java 8String.splitにも追加されます。

リファレンス実装

Java 7およびJava 8.の参照実装のPattern.splitのコードを比較しましょう。コードはバージョン7u40のgrepcodeから取得されます。 -b43および8-b132。

Java 7

public String[] split(CharSequence input, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<>();
    Matcher m = matcher(input);

    // Add segments before each match found
    while(m.find()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    int resultSize = matchList.size();
    if (limit == 0)
        while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
            resultSize--;
    String[] result = new String[resultSize];
    return matchList.subList(0, resultSize).toArray(result);
}

Java 8

public String[] split(CharSequence input, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<>();
    Matcher m = matcher(input);

    // Add segments before each match found
    while(m.find()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    int resultSize = matchList.size();
    if (limit == 0)
        while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
            resultSize--;
    String[] result = new String[resultSize];
    return matchList.subList(0, resultSize).toArray(result);
}

Java 8に次のコードを追加すると、入力文字列の先頭にあるゼロ長の一致が除外されます。これにより、上記の動作が説明されます。

            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }

互換性を維持する

Java 8以降での次の動作

splitがバージョン間で一貫して動作し、Java 8)の動作と互換性があるようにするには:

  1. 正規表現canが長さゼロの文字列に一致する場合、正規表現のthe end(?!\A)を追加し、元の正規表現を非キャプチャグループ(?:...) (必要であれば)。
  2. 正規表現ca n'tが長さゼロの文字列と一致する場合、何もする必要はありません。
  3. 正規表現が長さゼロの文字列と一致するかどうかわからない場合は、手順1の両方のアクションを実行します。

(?!\A)は、文字列が文字列の先頭で終わっていないことをチェックします。これは、文字列の先頭で一致が空の一致であることを意味します。

Java 7およびそれ以前の動作

splitを下位互換にするための一般的な解決策はありません。Java 7以前、独自のカスタム実装を指すようにsplitのすべてのインスタンスを置き換えること以外は。

83
nhahtdh

これは split(String regex, limit) のドキュメントで指定されています。

この文字列の先頭に正の幅の一致がある場合、結果の配列の先頭に空の先行部分文字列が含まれます。ただし、先頭の幅がゼロの一致では、このような空の先行部分文字列は生成されません。

"abc".split("")では、先頭にゼロ幅の一致があるため、先頭の空の部分文字列は結果の配列に含まれません。

ただし、2番目のスニペットでは、"a"正の幅の一致(この場合は1)があるため、空の先行部分文字列が期待どおりに含まれます。

(無関係なソースコードを削除)

30
Alexis C.

split() from Java 7 to Java 8。次のステートメントが追加されました。

この文字列の先頭に正の幅の一致がある場合、結果の配列の先頭に空の先行部分文字列が含まれます。 しかし、最初のゼロ幅のマッチはそのような空の先行部分文字列を決して生成しません。

(エンファシス鉱山)

空の文字列の分割は、先頭で幅がゼロの一致を生成するため、空の文字列は、上記の指定に従って、結果の配列の先頭に含まれません。対照的に、"a"で分割する2番目の例では、文字列の先頭でpositive-widthが一致するため、実際には空の文字列が先頭に含まれます結果の配列の。

14
arshajii