Patch: FYI: more String patches

Tom Tromey tromey@redhat.com
Sun Mar 30 06:42:00 GMT 2003


I'm checking this in on the trunk.

This is the next step in re-doing the String merge.
This patch adds some missing String methods from Classpath.
It also changes String to cache the hashcode.  This was discussed
when Eric wrote the patch, and Jeff did some benchmarks that showed
this to be a performance improvement.

Tom

Index: ChangeLog
from  Eric Blake  <ebb9@email.byu.edu>
	Tom Tromey  <tromey@redhat.com>

	* java/lang/natString.cc (hashCode): Use cachedHashCode.
	(init()): Removed.
	(charAt): Put index in exception.
	(contentEquals): New method.
	Include StringBuffer.h.
	* java/lang/String.java (cachedHashCode): New field.
	(String()): Follow classpath implementation.
	(init()): Removed.
	(contentEquals): Declare.
	(subSequence): Don't declare IndexOutIfBoundsException in throws
	clause.
	(matches, replaceFirst, replaceAll, split): New methods from
	Classpath.

Index: java/lang/String.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/String.java,v
retrieving revision 1.20
diff -u -r1.20 String.java
--- java/lang/String.java 30 Mar 2003 05:38:28 -0000 1.20
+++ java/lang/String.java 30 Mar 2003 06:21:59 -0000
@@ -44,6 +44,8 @@
 import java.lang.Comparable;
 import java.util.Comparator;
 import java.util.Locale;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 
 /**
  * Strings represent an immutable set of characters.  All String literals
@@ -87,6 +89,12 @@
   int count;
 
   /**
+   * Caches the result of hashCode().  If this value is zero, the hashcode
+   * is considered uncached (even if 0 is the correct hash value).
+   */
+  private int cachedHashCode;
+
+  /**
    * An implementation for {@link CASE_INSENSITIVE_ORDER}.
    * This must be {@link Serializable}. The class name is dictated by
    * compatibility with Sun's JDK.
@@ -137,9 +145,11 @@
    * Creates an empty String (length 0). Unless you really need a new object,
    * consider using <code>""</code> instead.
    */
-  public String ()
+  public String()
   {
-    init();
+    data = "".data;
+    boffset = 0;
+    count = 0;
   }
 
   /**
@@ -154,6 +164,7 @@
     data = str.data;
     boffset = str.boffset;
     count = str.count;
+    cachedHashCode = str.cachedHashCode;
   }
 
   /**
@@ -511,6 +522,17 @@
   public native boolean equals (Object anObject);
 
   /**
+   * Compares the given StringBuffer to this String. This is true if the
+   * StringBuffer has the same content as this String at this moment.
+   *
+   * @param buffer the StringBuffer to compare to
+   * @return true if StringBuffer has the same character sequence
+   * @throws NullPointerException if the given StringBuffer is null
+   * @since 1.4
+   */
+  public native boolean contentEquals(StringBuffer buffer);
+
+  /**
    * Compares a String to this String, ignoring case. This does not handle
    * multi-character capitalization exceptions; instead the comparison is
    * made on a character-by-character basis, and is true if:<br><ul>
@@ -815,7 +837,6 @@
    * @since 1.4
    */
   public CharSequence subSequence(int beginIndex, int endIndex)
-    throws IndexOutOfBoundsException
   {
     return substring(beginIndex, endIndex);
   }
@@ -841,6 +862,124 @@
   public native String replace (char oldChar, char newChar);
 
   /**
+   * Test if this String matches a regular expression. This is shorthand for
+   * <code>{@link Pattern}.matches(regex, this)</code>.
+   *
+   * @param regex the pattern to match
+   * @return true if the pattern matches
+   * @throws NullPointerException if regex is null
+   * @throws PatternSyntaxException if regex is invalid
+   * @see Pattern#matches(String, CharSequence)
+   * @since 1.4
+   */
+  public boolean matches(String regex)
+  {
+    return Pattern.matches(regex, this);
+  }
+
+  /**
+   * Replaces the first substring match of the regular expression with a
+   * given replacement. This is shorthand for <code>{@link Pattern}
+   *   .compile(regex).matcher(this).replaceFirst(replacement)</code>.
+   *
+   * @param regex the pattern to match
+   * @param replacement the replacement string
+   * @return the modified string
+   * @throws NullPointerException if regex or replacement is null
+   * @throws PatternSyntaxException if regex is invalid
+   * @see #replaceAll(String, String)
+   * @see Pattern#compile(String)
+   * @see Pattern#matcher(CharSequence)
+   * @see Matcher#replaceFirst(String)
+   * @since 1.4
+   */
+  public String replaceFirst(String regex, String replacement)
+  {
+    return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
+  }
+
+  /**
+   * Replaces all matching substrings of the regular expression with a
+   * given replacement. This is shorthand for <code>{@link Pattern}
+   *   .compile(regex).matcher(this).replaceAll(replacement)</code>.
+   *
+   * @param regex the pattern to match
+   * @param replacement the replacement string
+   * @return the modified string
+   * @throws NullPointerException if regex or replacement is null
+   * @throws PatternSyntaxException if regex is invalid
+   * @see #replaceFirst(String, String)
+   * @see Pattern#compile(String)
+   * @see Pattern#matcher(CharSequence)
+   * @see Matcher#replaceAll(String)
+   * @since 1.4
+   */
+  public String replaceAll(String regex, String replacement)
+  {
+    return Pattern.compile(regex).matcher(this).replaceAll(replacement);
+  }
+
+  /**
+   * Split this string around the matches of a regular expression. Each
+   * element of the returned array is the largest block of characters not
+   * terminated by the regular expression, in the order the matches are found.
+   *
+   * <p>The limit affects the length of the array. If it is positive, the
+   * array will contain at most n elements (n - 1 pattern matches). If
+   * negative, the array length is unlimited, but there can be trailing empty
+   * entries. if 0, the array length is unlimited, and trailing empty entries
+   * are discarded.
+   *
+   * <p>For example, splitting "boo:and:foo" yields:<br>
+   * <table border=0>
+   * <th><td>Regex</td> <td>Limit</td> <td>Result</td></th>
+   * <tr><td>":"</td>   <td>2</td>  <td>{ "boo", "and:foo" }</td></tr>
+   * <tr><td>":"</td>   <td>t</td>  <td>{ "boo", "and", "foo" }</td></tr>
+   * <tr><td>":"</td>   <td>-2</td> <td>{ "boo", "and", "foo" }</td></tr>
+   * <tr><td>"o"</td>   <td>5</td>  <td>{ "b", "", ":and:f", "", "" }</td></tr>
+   * <tr><td>"o"</td>   <td>-2</td> <td>{ "b", "", ":and:f", "", "" }</td></tr>
+   * <tr><td>"o"</td>   <td>0</td>  <td>{ "b", "", ":and:f" }</td></tr>
+   * </table>
+   *
+   * <p>This is shorthand for
+   * <code>{@link Pattern}.compile(regex).split(this, limit)</code>.
+   *
+   * @param regex the pattern to match
+   * @param limit the limit threshold
+   * @return the array of split strings
+   * @throws NullPointerException if regex or replacement is null
+   * @throws PatternSyntaxException if regex is invalid
+   * @see Pattern#compile(String)
+   * @see Pattern#split(CharSequence, int)
+   * @since 1.4
+   */
+  public String[] split(String regex, int limit)
+  {
+    return Pattern.compile(regex).split(this, limit);
+  }
+
+  /**
+   * Split this string around the matches of a regular expression. Each
+   * element of the returned array is the largest block of characters not
+   * terminated by the regular expression, in the order the matches are found.
+   * The array length is unlimited, and trailing empty entries are discarded,
+   * as though calling <code>split(regex, 0)</code>.
+   *
+   * @param regex the pattern to match
+   * @return the array of split strings
+   * @throws NullPointerException if regex or replacement is null
+   * @throws PatternSyntaxException if regex is invalid
+   * @see #split(String, int)
+   * @see Pattern#compile(String)
+   * @see Pattern#split(CharSequence, int)
+   * @since 1.4
+   */
+  public String[] split(String regex)
+  {
+    return Pattern.compile(regex).split(this, 0);
+  }
+
+  /**
    * Lowercases this String according to a particular locale. This uses
    * Unicode's special case mappings, as applied to the given Locale, so the
    * resulting string may be a different length.
@@ -1088,7 +1227,6 @@
   public native String intern ();
 
 
-  private native void init ();
   private native void init (char[] chars, int offset, int count,
 			    boolean dont_copy);
   private native void init (byte[] chars, int hibyte, int offset, int count);
Index: java/lang/natString.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natString.cc,v
retrieving revision 1.27
diff -u -r1.27 natString.cc
--- java/lang/natString.cc 13 Jun 2002 18:16:26 -0000 1.27
+++ java/lang/natString.cc 30 Mar 2003 06:22:00 -0000
@@ -1,6 +1,6 @@
 // natString.cc - Implementation of java.lang.String native methods.
 
-/* Copyright (C) 1998, 1999, 2000, 2001, 2002  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -20,6 +20,7 @@
 #include <java/lang/ArrayIndexOutOfBoundsException.h>
 #include <java/lang/StringIndexOutOfBoundsException.h>
 #include <java/lang/NullPointerException.h>
+#include <java/lang/StringBuffer.h>
 #include <java/io/ByteArrayOutputStream.h>
 #include <java/io/OutputStreamWriter.h>
 #include <java/io/ByteArrayInputStream.h>
@@ -102,7 +103,9 @@
 jint
 java::lang::String::hashCode()
 {
-  return hashChars(JvGetStringChars(this), length());
+  if (cachedHashCode == 0)
+    cachedHashCode = hashChars(JvGetStringChars(this), length());
+  return cachedHashCode;
 }
 
 jstring*
@@ -429,14 +432,6 @@
 }
 
 void
-java::lang::String::init ()
-{
-  count = 0;
-  boffset = sizeof(java::lang::String);
-  data = this;
-}
-
-void
 java::lang::String::init(jcharArray chars, jint offset, jint count,
 			 jboolean dont_copy)
 {
@@ -552,11 +547,30 @@
   return true;
 }
 
+jboolean
+java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
+{
+  if (buffer == NULL)
+    throw new NullPointerException;
+  JvSynchronize sync(buffer);
+  if (count != buffer->count)
+    return false;
+  if (data == buffer->value)
+    return true; // Possible if shared.
+  jint i = count;
+  jchar *xptr = JvGetStringChars(this);
+  jchar *yptr = elements(buffer->value);
+  while (--i >= 0)
+    if (*xptr++ != *yptr++)
+      return false;
+  return true;
+}
+
 jchar
 java::lang::String::charAt(jint i)
 {
   if (i < 0 || i >= count)
-    throw new java::lang::StringIndexOutOfBoundsException;
+    throw new java::lang::StringIndexOutOfBoundsException(i);
   return JvGetStringChars(this)[i];
 }
 



More information about the Java-patches mailing list