--- old/src/share/classes/java/lang/String.java 2013-02-16 22:32:08.260026292 -0800 +++ new/src/share/classes/java/lang/String.java 2013-02-16 22:32:08.016026279 -0800 @@ -25,6 +25,7 @@ package java.lang; +import java.io.ObjectStreamException; import java.io.ObjectStreamField; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; @@ -110,7 +111,7 @@ public final class String implements java.io.Serializable, Comparable, CharSequence { /** The value is used for character storage. */ - private final char value[]; + final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 @@ -965,6 +966,7 @@ * @see #compareTo(String) * @see #equalsIgnoreCase(String) */ + @Override public boolean equals(Object anObject) { if (this == anObject) { return true; @@ -983,6 +985,9 @@ } return true; } + } else if (anObject instanceof SubSequence) { + // turn the tables to keep this method smaller. + return anObject.equals(this); } return false; } @@ -1159,6 +1164,18 @@ return len1 - len2; } +// This is the method we want instead of the default bridge. +// public int compareTo(Object other) { +// if(other instanceof String) { +// return compareTo((String) other); +// } else if (other instanceof SubSequence) { +// // delegate to keep this method small. +// return - ((SubSequence) other).compareTo(this); +// } else { +// throw new ClassCastException(); +// } +// } +// /** * A Comparator that orders {@code String} objects as by * {@code compareToIgnoreCase}. This comparator is serializable. @@ -1962,18 +1979,7 @@ /** * Returns a new character sequence that is a subsequence of this sequence. * - *

An invocation of this method of the form - * - *

-     * str.subSequence(begin, end)
- * - * behaves in exactly the same way as the invocation - * - *
-     * str.substring(begin, end)
- * - * This method is defined so that the {@code String} class can implement - * the {@link CharSequence} interface.

+ * @implNote The character sequence refers to the original String. * * @param beginIndex the begin index, inclusive. * @param endIndex the end index, exclusive. @@ -1987,8 +1993,210 @@ * @since 1.4 * @spec JSR-51 */ + @Override public CharSequence subSequence(int beginIndex, int endIndex) { - return this.substring(beginIndex, endIndex); + if (beginIndex < 0) { + throw new StringIndexOutOfBoundsException(beginIndex); + } + if (endIndex > value.length) { + throw new StringIndexOutOfBoundsException(endIndex); + } + int subLen = endIndex - beginIndex; + if (subLen < 0) { + throw new StringIndexOutOfBoundsException(subLen); + } + + return (subLen == value.length) + ? this + : new SubSequence(this, beginIndex, subLen); + } + + /** + * A CharSequence implemented as a sub-sequence of a String. + */ + private final static class SubSequence implements + java.io.Serializable, CharSequence, Comparable { + + /** + * The String of which we are a sub-sequence. + */ + private final String source; + + /** + * The offset within the String of our first character. + */ + private final int offset; + + /** + * The number of characters in this sub-sequence. + */ + private final int count; + + /** + * Cached hash code value. + */ + private int hashCache = 0; + + /** + * Construct a new sub-sequence. + * + * @implNote Input values are not validated. + * + * @param source The String of which we are a sub-sequence. + * @param offset The offset within the String of our first character. + * @param count The number of characters in this sub-sequence. + */ + SubSequence(String source, int offset, int count) { + this.source = source; + this.offset = offset; + this.count = count; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + // it's me! + return true; + } + + final char[] val1 = source.value; + int offset1 = offset; + final char[] val2; + int offset2; + int each; + if (other instanceof SubSequence) { + SubSequence likeMe = (SubSequence)other; + val2 = likeMe.source.value; + offset2 = likeMe.offset; + each = likeMe.count; + } else if (other instanceof String) { + String similar = (String)other; + val2 = similar.value; + offset2 = 0; + each = similar.value.length; + } else { + // not of recognized type. + return false; + } + + if (each != count) { + // not the same length + return false; + } + + offset1 += each; + offset2 += each; + while (--each >= 0) { + if (val1[--offset1] != val2[--offset2]) { + // unequal char + return false; + } + } + + // chars were all equal. + return true; + } + + /** + * Return the hash code value for this object. + * + * @implSpec The hash code of a SubSequence is the same as that of a + * String containing the same characters. + * + * @return a hash code value for this object. + */ + @Override + public int hashCode() { + int h = hashCache; + if (h == 0 && count > 0) { + char val[] = source.value; // avoid getfield opcode + for (int i = 0; i < count; i++) { + h = 31 * h + val[offset + i]; + } + + // harmless data race updating hashCache. + hashCache = h; + } + + return h; + } + + @Override + public String toString() { + return new String(source.value, offset, count); + } + + public boolean isEmpty() { + return count == 0; + } + + + @Override + public int compareTo(CharSequence other) { + int otherLen = other.length(); + for(int each=0; each < count; each++) { + if(each >= otherLen) { + return 1; + } + + int diff = other.charAt(each) - source.value[offset+each]; + + if(0 == diff) { + continue; + } + + return diff; + } + + return (otherLen > count) ? -1 : 0; + } + + @Override + public int length() { + return count; + } + + @Override + public char charAt(int index) { + if(index < 0 || index >= count) { + throw new IndexOutOfBoundsException(); + } + return source.value[offset+index]; + } + + @Override + public CharSequence subSequence(int start, int end) { + int len = end - start; + if (start < 0 || + end < start || + len > count) { + throw new IndexOutOfBoundsException(); + } + + if (0 == len) { + // it's empty. + return new String(); + } + + if(start == 0 && len == count) { + // exactly the same sequence. + return this; + } + + // create an even smaller sub-sequence + return new SubSequence(source, offset+start, len); + } + + /** + * {@inheritDoc} + * + * @implNote We replace this sub-sequence with a String containing the + * same sequence of characters. + */ + public Object writeReplace() throws ObjectStreamException { + // It's better to just replace with string. + return toString(); + } } /**