29

谁能解释下面的代码String.java,特别是为什么有三个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 技术标准

另外,由于自然语言的变幻莫测,也会出现两个不同的Unicode字符大小写相同的情况

因此,仅比较两个字符的大写是不够的,因为它们可能有不同的大写和相同的小写

简单的蛮力检查给出了一些结果。检查例如代码点 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

通常,我们希望转换一次案例并进行比较并完成它。但是,代码将大小写转换了两次,原因在注释中说明了另一种方法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)和 ascii“i”。它们不同,它们的小写不同,但它们共享相同的大写字母“I”。

最后,将大写 I 与点 "İ" 与小的无点 i "ı" 进行比较。它们的大写字母(“İ”与“I”)和它们的小写字母(“i”与“ı”)都不匹配,但它们的大写字母的小写字母相同(“I”)。如果出现这种现象,我发现了另一种情况,即希腊字母“ϴ”和“ϑ”(字符 1012 和 977)。

所以真正的不区分大小写的比较甚至不能检查原始字符的大小写,而必须检查大写的小写。

于 2014-08-26T19:21:20.163 回答
5

考虑以下字符:fF。初始if语句将返回false,因为它们不匹配。但是,如果将两个字符都大写,则会得到FF。然后他们会匹配。c比如说,和,情况并非如此G

该代码是有效的。如果两个字符已经匹配,则无需大写(因此是第一个if语句)。但是,如果它们不匹配,我们需要检查它们是否仅在大小写上有所不同(因此是第二if条语句)。

最后的if语句用于某些字母表(例如格鲁吉亚语),其中大写是一件复杂的事情。老实说,我不太了解它是如何工作的(相信甲骨文就是这样做的!)。

于 2013-03-20T08:43:38.387 回答
0

在上述不区分大小写比较的情况下,假设s1="Apple" 和 s2="apple" 在这种情况下 'A'!='a' 因为两个字符的 ascii 值不同,所以它将两个字符都更改为大写然后再次比较,然后循环继续获得 n1-n2=0 的最终值,因此字符串变得相同。假设字符完全不相等,第二次检查

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

返回两个字符的 ascii 值之差。

于 2013-03-20T08:46:47.337 回答