29

から次のコードを説明できる人はいますか?String.java具体的には、3 つのステートメントがあるのはなぜifですか?//1//2//3

private static class CaseInsensitiveComparator
                     implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;

    public int compare(String s1, String s2) {
        int n1=s1.length(), n2=s2.length();
        for (int i1=0, i2=0; i1<n1 && i2<n2; i1++, i2++) {
            char c1 = s1.charAt(i1);
            char c2 = s2.charAt(i2);
            if (c1 != c2) {/////////////////////////1
                c1 = Character.toUpperCase(c1);
                c2 = Character.toUpperCase(c2);
                if (c1 != c2) {/////////////////////////2
                    c1 = Character.toLowerCase(c1);
                    c2 = Character.toLowerCase(c2);
                    if (c1 != c2) {/////////////////////////3
                        return c1 - c2;
                    }
                }
            }
        }
        return n1 - n2;
    }
}
4

5 に答える 5

41

Unicode 技術標準から:

さらに、自然言語の気まぐれにより、2 つの異なる Unicode 文字が同じ大文字または小文字を持つ状況があります。

したがって、2 つの文字の大文字のみを比較するだけでは十分ではありません。大文字が異なり、小文字が同じである可能性があるためです。

単純なブルート フォース チェックでいくつかの結果が得られます。コード ポイント 73 と 304 の例を確認します。

char ch1 = (char) 73; //LATIN CAPITAL LETTER I
char ch2 = (char) 304; //LATIN CAPITAL LETTER I WITH DOT ABOVE
System.out.println(ch1==ch2);
System.out.println(Character.toUpperCase(ch1)==Character.toUpperCase(ch2));
System.out.println(Character.toLowerCase(ch1)==Character.toLowerCase(ch2));

出力:

false
false
true

したがって、「İ」と「I」は等しくありません。どちらの文字も大文字です。しかし、それらは同じ小文字「i」を共有しているため、大文字と小文字を区別しない比較でそれらを同じ値として扱う理由が得られます。

于 2013-03-20T08:47:24.020 に答える
31

通常、ケースを一度変換して比較し、それで完了することを期待します。ただし、コードはケースを 2 回変換し、その理由は別のメソッドのコメントに記載されていますpublic boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)

残念ながら、大文字への変換は、大文字と小文字の変換に関する奇妙な規則があるグルジア語のアルファベットでは正しく機能しません。そのため、終了する前に最後のチェックを行う必要があります。


付録

のコードは のコードとregionMatchesは少し違いますがCaseInsenstiveComparator、基本的には同じことを行います。クロスチェックの目的で、メソッドの完全なコードを以下に引用します。

public boolean regionMatches(boolean ignoreCase, int toffset,
                       String other, int ooffset, int len) {
    char ta[] = value;
    int to = offset + toffset;
    char pa[] = other.value;
    int po = other.offset + ooffset;
    // Note: toffset, ooffset, or len might be near -1>>>1.
    if ((ooffset < 0) || (toffset < 0) || (toffset > (long)count - len) ||
            (ooffset > (long)other.count - len)) {
        return false;
    }
    while (len-- > 0) {
        char c1 = ta[to++];
        char c2 = pa[po++];
        if (c1 == c2) {
            continue;
        }
        if (ignoreCase) {
            // If characters don't match but case may be ignored,
            // try converting both characters to uppercase.
            // If the results match, then the comparison scan should
            // continue.
            char u1 = Character.toUpperCase(c1);
            char u2 = Character.toUpperCase(c2);
            if (u1 == u2) {
                continue;
            }
            // Unfortunately, conversion to uppercase does not work properly
            // for the Georgian alphabet, which has strange rules about case
            // conversion.  So we need to make one last check before
            // exiting.
            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
                continue;
            }
        }
        return false;
    }
    return true;
}
于 2013-03-20T08:46:34.483 に答える
20

別の回答では、デフォルトのロケールは、大文字のみを比較するだけでは不十分な理由の例をすでに示しています。つまり、ASCII文字「I」とドット「İ」を含む大文字のIです。

大文字よりも多くのケースを検出する場合、大文字と小文字の両方ではなく、小文字のみを比較しないのはなぜでしょうか? 答えは、より多くのケースを検出するのではなく、単に別のケースを検出するということです。

文字 "ı" ( (char)305、小さいドットなしの i) とアスキー "i" を取ります。それらは異なり、小文字は異なりますが、大文字の「I」は同じです。

そして最後に、大文字の I にドット "İ" と小さなドットのない i "ı" を比較します。大文字 ("İ" と "I") も小文字 ("i" と "ı") も一致しませんが、大文字の小文字は同じです ("I")。この現象は、ギリシャ文字の "ϴ" と "ϑ" (char 1012 と 977) にも見られます。

したがって、真の大文字小文字を区別しない比較では、元の文字の大文字と小文字をチェックすることさえできませんが、大文字の小文字をチェックする必要があります。

于 2014-08-26T19:21:20.163 に答える
5

次の文字を考えてみましょう:fF. 一致しないため、最初のifステートメントが返されます。falseただし、両方の文字を大文字にするFと、 とF. その後、それらは一致します。c同じことは、たとえば、およびには当てはまりませんG

コードは効率的です。すでに一致している場合は、両方の文字を大文字にする必要はありません (したがって、最初のifステートメント)。ただし、一致しない場合は、大文字と小文字のみが異なるかどうかを確認する必要があります (したがって、2 番目のifステートメント)。

最終ifステートメントは、大文字化が複雑な問題である特定のアルファベット (グルジア語など) に使用されます。正直なところ、私はそれがどのように機能するかについてあまり知りません (Oracle が機能することを信じてください!)。

于 2013-03-20T08:43:38.387 に答える
0

上記の大文字と小文字を区別しない比較では、s1="Apple" と s2="apple" を想定し ています。この場合、'A'!='a' は両方の文字の ascii 値が異なるため、両方の文字を大文字に変更します。再び比較すると、ループは n1-n2=0 の最終値を取得し続けるため、文字列は同じになります.文字がまったく等しくない場合、2番目のチェック

if (c1 != c2) {
return c1 - c2;
}

2 つの文字の ASCII 値の差を返します。

于 2013-03-20T08:46:47.337 に答える