--- old/src/java.base/share/classes/java/lang/String.java 2016-03-09 16:26:46.785045514 +0100 +++ new/src/java.base/share/classes/java/lang/String.java 2016-03-09 16:26:46.697045030 +0100 @@ -36,6 +36,7 @@ import java.util.Objects; import java.util.Spliterator; import java.util.StringJoiner; +import java.util.function.ToIntFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -1229,11 +1230,24 @@ */ public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); + + /** + * A hashCode computing function that is consistent with + * {@link #CASE_INSENSITIVE_ORDER} {@code Comparator}. + * + * @since 9 + */ + @SuppressWarnings("unchecked") + public static final ToIntFunction CASE_INSENSITIVE_HASHER + = (ToIntFunction) CASE_INSENSITIVE_ORDER; + private static class CaseInsensitiveComparator - implements Comparator, java.io.Serializable { + implements Comparator, java.io.Serializable, + ToIntFunction /* hasher */ { // use serialVersionUID from JDK 1.2.2 for interoperability private static final long serialVersionUID = 8575799808933029326L; + @Override public int compare(String s1, String s2) { byte v1[] = s1.value; byte v2[] = s2.value; @@ -1245,6 +1259,15 @@ : StringUTF16.compareToCI_Latin1(v1, v2); } + /** + * Compute hashCode consistent with {@link #compare(String, String)} + */ + @Override + public int applyAsInt(String s) { + return s.isLatin1() ? StringLatin1.hashCodeCI(s.value) + : StringUTF16.hashCodeCI(s.value); + } + /** Replaces the de-serialized object. */ private Object readResolve() { return CASE_INSENSITIVE_ORDER; } } --- old/src/java.base/share/classes/java/lang/StringLatin1.java 2016-03-09 16:26:47.048046961 +0100 +++ new/src/java.base/share/classes/java/lang/StringLatin1.java 2016-03-09 16:26:46.961046482 +0100 @@ -148,6 +148,19 @@ return len1 - len2; } + /** + * hashCode consistent with {@link #compareToCI} + */ + public static int hashCodeCI(byte[] value) { + int h = 0; + for (byte v : value) { + char cu = (char) CharacterDataLatin1.instance.toUpperCase(v & 0xff); + char cl = (char) CharacterDataLatin1.instance.toLowerCase(cu); + h = 31 * h + cl; + } + return h; + } + public static int compareToCI_UTF16(byte[] value, byte[] other) { int len1 = length(value); int len2 = StringUTF16.length(other); --- old/src/java.base/share/classes/java/lang/StringUTF16.java 2016-03-09 16:26:47.281048243 +0100 +++ new/src/java.base/share/classes/java/lang/StringUTF16.java 2016-03-09 16:26:47.190047742 +0100 @@ -292,6 +292,20 @@ return len1 - len2; } + /** + * hashCode consistent with {@link #compareToCI} + */ + public static int hashCodeCI(byte[] value) { + int len = length(value); + int h = 0; + for (int k = 0; k < len; k++) { + char cu = (char) CharacterDataLatin1.instance.toUpperCase(getChar(value, k)); + char cl = (char) CharacterDataLatin1.instance.toLowerCase(cu); + h = 31 * h + cl; + } + return h; + } + public static int compareToCI_Latin1(byte[] value, byte[] other) { int len1 = length(value); int len2 = StringLatin1.length(other); --- old/src/java.base/share/classes/java/util/jar/Attributes.java 2016-03-09 16:26:47.560049778 +0100 +++ new/src/java.base/share/classes/java/util/jar/Attributes.java 2016-03-09 16:26:47.435049090 +0100 @@ -513,7 +513,7 @@ */ public int hashCode() { if (hashCode == -1) { - hashCode = name.toLowerCase(Locale.ROOT).hashCode(); + hashCode = String.CASE_INSENSITIVE_HASHER.applyAsInt(name); } return hashCode; }