Hello all;

In Java 7u6 there was a significant change in the implementation of 
java.lang.String (JDK-6924259). This was done to reduce the size of String 
instances and it has been generally regarded as a positive change. As with 
almost any significant change to a class as core to Java as String there have 
also been applications negatively impacted. Most of the problems involve 
applications which make heavy use of String.substring() as sub-string instances 
now involve creation of their own copies of the backing characters.

There have been previous discussions of mitigations to the 6924259 change in 
String.substring() behaviour. These discussions haven't come to positive 
conclusions mostly because they generally require too many changes to the 
specification or behaviour of String. So here's another proposal (enclosed) 
that doesn't change the behaviour of any existing classes. It adds two new 
methods to CharSequence to create sub-sequence views of character sequences. 
The size of sub-sequence instances very closely matches the size of pre-6924259 
String instances and indeed the implementation has the same pre-6924259 
limitations, namely that the entire source CharSequence remains alive as long 
as the sub-sequence is referenced.

Unlike pre-6924259 the CharSubSequenceView can not be reliably compared via 
equals() to String instances and it is unsuitable for use as a hash map key. 

With these benefits and caveats in mind, would you use this?

Mike

diff -r 66f582158e1c src/share/classes/java/lang/CharSequence.java
--- a/src/share/classes/java/lang/CharSequence.java     Wed Jul 16 20:43:53 
2014 +0100
+++ b/src/share/classes/java/lang/CharSequence.java     Wed Jul 16 16:58:52 
2014 -0700
@@ -25,11 +25,14 @@
 
 package java.lang;
 
+import java.io.Serializable;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.PrimitiveIterator;
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
 import java.util.stream.IntStream;
 import java.util.stream.StreamSupport;
 
@@ -231,4 +234,114 @@
                 Spliterator.ORDERED,
                 false);
     }
+
+    /**
+     * Provides a sub-sequence view on a character sequence. Changes in the
+     * source will be reflected in the sub-sequence. The sub-sequence must, at
+     * all times, be a proper sub-sequence of the source character sequence.
+     *
+     * @since 1.9
+     */
+    static final class CharSubSequenceView implements CharSequence, 
Serializable {
+
+        private final CharSequence source;
+        private final int fromInclusive;
+        private final IntSupplier toExclusive;
+
+        CharSubSequenceView(CharSequence source, int fromInclusive, int 
toExclusive) {
+            this(source, fromInclusive, () -> toExclusive);
+        }
+
+        CharSubSequenceView(CharSequence source, int fromInclusive, 
IntSupplier toExclusive) {
+            this.source = Objects.requireNonNull(source);
+            if(fromInclusive < 0 || fromInclusive >= source.length() ||
+               toExclusive.getAsInt() < fromInclusive || 
toExclusive.getAsInt() > source.length()) {
+                throw new IllegalArgumentException("Invalid index");
+            }
+            this.fromInclusive = fromInclusive;
+            this.toExclusive = toExclusive;
+        }
+
+        @Override
+        public int length() {
+            return toExclusive.getAsInt() - fromInclusive;
+        }
+
+        @Override
+        public char charAt(int index) {
+            if(index >= length()) {
+                throw new IllegalArgumentException("Invalid Index");
+            }
+            //
+            return source.charAt(fromInclusive + index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+           if (end > length()) {
+               throw new IllegalArgumentException("Invalid Index");
+           }
+           return source.subSequence(fromInclusive + start, fromInclusive + 
end);
+        }
+
+        @Override
+        public String toString() {
+            int len = length();
+            char[] chars = new char[len];
+            for(int each = 0; each < len; each++) {
+                chars[each] = charAt(each);
+            }
+            return new String(chars, true);
+        }
+    }
+
+    /**
+     * Returns as a character sequence the specified sub-sequence view of the
+     * provided source character sequence. Changes in the source will be
+     * reflected in the sub-sequence. The sub-sequence must, at all times, be
+     * a proper sub-sequence of the source character sequence.
+     *
+     * @param source The character sequence from which the sub-sequence is
+     * derived.
+     * @param startInclusive The index of the character in the source character
+     * sequence which will be the first character in the sub-sequence.
+     * @param endExclusive The index after the last the character in the source
+     * character sequence which will be the last character in the sub-sequence
+     * @return the character sub-sequence.
+     * @since 1.9
+     */
+    static CharSequence subSequenceView(CharSequence source, int 
startInclusive, int endExclusive) {
+        return new CharSubSequenceView(source, startInclusive, endExclusive);
+    }
+
+    /**
+     * Returns as a character sequence the specified sub-sequence view of the
+     * provided source character sequence. Changes in the source will be
+     * reflected in the sub-sequence. The sub-sequence must, at all times, be
+     * a proper sub-sequence of the source character sequence. This variation
+     * allows for the size of the sub-sequence to vary, usually to follow the
+     * size of a growing character sequence.
+     *
+     * @apiNote The most common usage of this subSequence is to follow changes
+     * in the size of the source.
+     * {@code
+     * StringBuilder source = new StringBuilder("prefix:");
+     * CharSeqence toEnd = CharSequence.subSequence(source, 7, source::length);
+     * }
+     * In this example the value of {@code toEnd} will always be a sub-sequence
+     * of {@code source} but will omit the first 7 characters.
+     *
+     * @param source The character sequence from which the sub-sequence is
+     * derived.
+     * @param startInclusive The index of the character in the source character
+     * sequence which will be the first character in the sub-sequence.
+     * @param endExclusive A supplier which returns the index after the last 
the
+     * character in the source character sequence which will be the last
+     * character in the sub-sequence
+     * @return the character sub-sequence.
+     * @since 1.9
+     */
+    static CharSequence subSequenceView(CharSequence source, int 
startInclusive, IntSupplier endExclusive) {
+        return new CharSubSequenceView(source, startInclusive, endExclusive);
+    }
 }

Reply via email to