Title: Strings
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

String and StringBuffer classes

It's hard to do much in java without encountering the String class. We have been using the String class, but in this set of notes we will cover String details.

String Facts:

- Strings store Unicode. Unicode is a 2 byte character encoding system that includes thousands of international language symbols. This is especially important for Oriental languages. The Primitive char is also used to store a Unicode character. This is quite different from the typical characters in other languages that are 8 bits and only work well with English and languages from Europe.
- Strings are immutable which means that once you create a String class, you can't change it's contents. If you look at the methods in the String class, you will not see any methods which change the current String's contents. You will only find methods that return a new String class.


    // See if you can draw a picture of the memory associated with the following.
    // If you don't understand, have your instructor draw a picture of what is going on
    String y="yes";
    String n="no";
    String m="maybe";
    String s = "I vote ";
    s = s + y;
    s = s + n;
    m = "Possibly";
    s += m;


- If you want something like a String class that can be modified, you may want to use the StringBuffer class.
- When a string appears on either side of the + operator, the result is a String. If one of the operands is class other than the string, the toString( ) method is called on any arguments that are not Strings. If one of the operands is a primitive like double, float, int, short, byte, char the result of the + operator is also a String.

-
  int i=1;
  double d=1.1;
  boolean b = false;
  Rectangle r = new Rectangle;
  String result;

  //legal Java statements
  result = "i = " + i;
  result = "d = " + d;
  result = "b = " + b;
  result = "r = " + r; //r.toString() is called here


- Every Java class can be converted to a string. This is because every class is derived from Object, and the Object class contains a method called toString(). If you wrote the class yourself, you probably want to override the Object toString() method to get a more meaningful string representation of your class. We did this previously with the Rectangle class ----
Refresh your memory
- Because the String class is such a key class in Java, many shortcuts have been provided that don't work for any other class. For classes other than the String class, there are no shortcuts. Here is a sampling of some of the shortcuts:


 // Shortcut #1
 String result = "initial";
 // the equivalent code for Shortcut #1
 String result = new String("initial");

 // Shortcut #2
 String [] names = {"Fred", "John", "Sam"};
 // the equivalent code for Shortcut #2
 String [] names = new String[3];
 names[0] = new String("Fred");
 names[1] = new String("John");
 names[2] = new String("Sam");

 // Shortcut #3
 int i = 1;
 String result2;
 result2 = "i=" + 1;
 // the equivalent code for Shortcut #3
 StringBuffer temp;
 temp = new StringBuffer("i=");
 temp.append(i);
 result2 = temp.toString();


- The java compiler will perform certain optimizations to save String space. These optimizations will sometimes make it look like the == operator will work with strings. Normally, if you are comparing 2 String values to find out if they have the same content, you will want to use the equals method. We discussed this earlier:
Review of == versus equals


    String a = "java";
    String b = "java"; // compiler optimization will not duplicate previous space
    String x = "ja";
    String y = "va";
    String c= x + y; // computed at run time so it has to be new space.
    if (a == b)
       System.out.println("This statement will print");
    if (a == c)
    	System.out.println("This statement will NOT print");
    if (a.equals(c))
        System.out.println("This statement will print");


StringBuffer

The StringBuffer class is like a String class that can be changed (mutable which is the same as NOT immutable). The size of the StringBuffer in memory automatically grows as needed. Although you don't have to worry about the size of your StringBuffer in memory, you can give the StringBuffer some clues as to how much memory it will need and this will give your program some efficiency gains.

Some of the methods in the String class are the same as in the StringBuffer class. However, most of the methods are different. The String class and the StringBuffer class were created to work together. We will illustrate this with examples.

I would recommend looking at the On-Line help for String and StringBuffer. Both classes can be found in the java.lang package. The following is a subset of the overall capability, and lists the most common characteristics that I have used in String and StringBuffer:

Which Class | Method/Constructor | Notes
String | String( ) | Default Constructor
String | String(String y) | You are most familiar with this with statements like: String result = "Fred";
String | String(StringBuffer y) | Makes it easy to create a String from a StringBuffer
StringBuffer | StringBuffer( ) | Default Constructor. Default capacity is 16.
StringBuffer | StringBuffer(int length) | Empty buffer, but reserving some specified space. By choosing a good estimate of the length (capacity) , you will gain efficiency. If you are wrong, its OK, because space will automatically get reallocated.
StringBuffer | StringBuffer(String y) | Makes it easy to create a StringBuffer from a String
BOTH | char charAt(int index) | return a character at a specified index. The first character in the buffer is index = 0. The last character in the string is at (length - 1)
BOTH
int length( )
String s = "be Good";
int len;
len = s.length();
//len contains 7
BOTH | String substring(int start) | String s = "be Good"; String s2 = s.substring(3); // s2 contains "Good"
BOTH | String substring(int start, int end) | String s= "be Good"; String s2 = s.substring(3,5); // s2 contains "Go"
BOTH | toString( ) | Rarely called directly for the String class, but important in making things work the way you expect. On the otherhand, using this method on a StringBuffer class gives you a quick way to create a String from your StringBuffer.
String | boolean equals(Object y) | Overriden from the Object class. Normally, this is what you want to use to compare 2 strings.
String | boolean equalsIgnoreCase(String y) | Case Insensitive String Comparison.
String
int indexOf(int ch)
int lastIndexOf(int ch)
String s="day by day";
int index, index2;
index = s.indexOf('y');
index2 = s.lastIndexOf('y');
// index contains 2
// index2 contains 9
index = s.indexOf('z');
index2 = s.lastIndexOf('z');
// index and index2 contain -1
String
int indexOf(int ch, int fromIndex)
int lastIndexOf(int ch, int fromIndex)
String s="day by day";
int index, index2;
index = s.indexOf('y', 3);
index2 = s.lastIndexOf('y', 8);
// index contains 5
// index2 contains 5
String
int indexOf(String str)
int lastIndexOf(String str)
String s="day by day";
int index, index2;
index = s.indexOf("day");
index2 = s.lastIndexOf("day");
// index contains 0
// index2 contains 7
index = s.indexOf("days");
index2=s.lastIndexOf("days");
// index and index2 contain -1
String
int indexOf(String str, int fromIndex)
int lastIndexOf(String str, int fromIndex)
String s="day by day";
int index, index2;
index = s.indexOf("day",1);
index2 = s.lastIndexOf("day",6);
// index contains 7
// index2 contains 0
index = s.indexOf("days",1);
index2=s.lastIndexOf("days",7);
// index and index2 contain -1
String | String trim( ) | removes white space from the beginning and the end of the String
String
String String.valueOf(int i)
String String.valueOf(double d)
etc. for all primitive types
String String.valueOf(Object obj)
valueOf is a static method. A less efficient way to accomplish the same goal is:
String result;
double d = 1.2;
result = "" + d;
String
int compareTo(String y)
int compareToIgnoreCase(String y)
Compares 2 strings lexicographically.

0 return ==> equal
positive return ==> y is precedes this string

negatve return ==> y follows this string

String x = "abc";
String y = "bc";
String s "should print this";

if (x.compareTo(y) < 0)
System.out.println(s);
String
boolean startsWith(String y)
boolean endsWith(String y)
String y = "MsWindows.txt";
boolean b1, b2;
b1 = y.startsWith("Ms");
b2 = y.endsWith(".txt");
// Both b1 and b2 should be true
String | String toUpperCase( ) String toLowerCase( ) |
StringBuffer | void setLength(int newLength) | Tells StringBuffer to allocate more space --- doesn't actually fill the space
StringBuffer | int capacity( ) | Returns current allocated size of StringBuffer
StringBuffer | void setCharAt(int index, char c) | Replaces a character at a specified location
StringBuffer
FAMILY of append:
append(boolean b)
append(int i)
append(double d)
etc.
append(String s)
append(Object obj)
Adds the desired object to the end of the StringBuffer. The object needs to be converted to a String before adding it to the buffer.

StringBuffer sb = new    StringBuffer("012");
sb.append(3);
// sb now contains "0123"
StringBuffer
FAMILY of insert:
insert(int offset, boolean b)
insert(int offset, int i)
insert(int offset, double d)
etc.
insert(int offset, String s)
insert(int offset, Object obj)
Inserts the desired object (of course converted to a String) into the String Buffer starting at the specified offset (first character in the buffer is at index 0)

StringBuffer sb= new    StringBuffer ("016");
sb.insert(2, "2345");
// sb now contains "0123456"
StringBuffer
delCharAt(int index)
delete(int start, int end)
StringBuffer sb = new    StringBuffer("01xxx234556");
sb.delCharAt(8);
// sb now contains "01xxx23456"
sb.delete(2,5);
// sb now contains "0123456"
StringBuffer | replace(int start, int end, String str) | StringBuffer sb= new    StringBuffer("01xxx56"); sb.replace(2,5,"234"); // sb now contains "0123456"

Here is a sample exercise (FormLetter) solved using some of the above methods:

import tools.*;

class FormLetter
{
  String name, occupation;
  String name_key = "****Customer****";
  String occupation_key = "****Occupation****";

  String [] letter = {
    "Hi ****Customer****,",
    "   We think you would benefit from our offer.  We know that ",
    "****Occupation****s are very intelligent people.  We think that",
    "your intelligence will help you to see how good an offer this is.",
    "****Customer****, after you have purchased our offer you will " ,
    "wonder how you ever lived without this unique offer.  We believe ",
    "that other ****Occupation****s will be asking where you acquired ",
    "our unique product.  ****Customer****, please call me at ",
    "(111) 111-1111 if you have any further questions.",
    "Sincerely, ",
    "Joe GoodGuy from Done Right Inc."};

  FormLetter(String n, String o)
  {
    name = n;
    occupation = o;
  }

  String replaceWith(String line, String find, String replace)
  {
    int index, start=0;
    StringBuffer sb = new StringBuffer(200);

    do
    {
        index = line.indexOf(find, start);
        if (index >= 0)
        {
            sb.append( line.substring(start, index));
            sb.append(replace);
            start = index + find.length();
        }
        else
        {
            sb.append(line.substring(start));
        }
    }while (index >= 0);

    return sb.toString();
  }

  void print()
  {
     String str1, str2;
     for (int i=0; i < letter.length; i++)
     {
        str1 = replaceWith(letter[i], name_key, name);
        str2 = replaceWith(str1, occupation_key, occupation);
        System.out.println(str2);
     }
  }

  public static void main(String[] args)
  {
    FormLetter fletter;
    MyInput in = new MyInput();
    String name, occupation;

    System.out.println("Please enter in the CustomerName");
    name = in.get();
    System.out.println("Please enter in the Customer's Occupation");
    occupation = in.get();

    fletter = new FormLetter(name, occupation);
    fletter.print();
  }
}


Rework the above example to use the replace method instead of the append method.

The following is what we came up with in the last class using these notes:


import tools.*;

class FormLetter
{
  String name, occupation;
  String name_key = "****Customer****";
  String occupation_key = "****Occupation****";

  String [] letter = {
    "Hi ****Customer****,",
    "   We think you would benefit from our offer.  We know that ",
    "****Occupation****s are very intelligent people.  We think that",
    "your intelligence will help you to see how good an offer this is.",
    "****Customer****, after you have purchased our offer you will " ,
    "wonder how you ever lived without this unique offer.  We believe ",
    "that other ****Occupation****s will be asking where you acquired ",
    "our unique product.  ****Customer****, please call me at ",
    "(111) 111-1111 if you have any further questions.",
    "Sincerely, ",
    "Joe GoodGuy from Done Right Inc."};

  FormLetter(String n, String o)
  {
    name = n;
    occupation = o;
  }
// Clem's version of indexOf
 int indexOf(StringBuffer sb, String find, int start)
    {
        int len = sb.length();
        int findlen = find.length();
        char c = find.charAt(0);
        len -= findlen; // Done when we don't have enough chars to match
        for (  ; start <= len; start++)
        {
            if (sb.charAt(start) != c) continue; // speedup
            for (int i=0; i < findlen; i++)
            {
                if (find.charAt(i) != sb.charAt(start+i))
                    break;
                if (i == findlen -1)
                    return start;
            }
        }
        return -1;
    }


  String replaceWith(String line, String find, String replace)
  {
    int index, start=0;
    StringBuffer sb = new StringBuffer(line);
    int len = find.length();
    int len2 = replace.length();
    do
    {
        index = indexOf(sb, find, start);
        if (index >= 0)
        {
            sb.replace(index, index + len, replace);
            start = index + len2;
        }
    }while (index >= 0);

    return sb.toString();
  }

  void print()
  {
     String str1, str2;
     for (int i=0; i < letter.length; i++)
     {
        str1 = replaceWith(letter[i], name_key, name);
        str2 = replaceWith(str1, occupation_key, occupation);
        System.out.println(str2);
     }
  }

  public static void main(String[] args)
  {
    FormLetter fletter;
    MyInput in = new MyInput();
    String name, occupation;

    System.out.println("Please enter in the CustomerName");
    name = in.get();
    System.out.println("Please enter in the Customer's Occupation");
    occupation = in.get();

    fletter = new FormLetter(name, occupation);
    fletter.print();
  }
}



I would helpful to do some of the following exercises or get your instructor to show you how to do it.

Exercises:

1. Use just the String class for this example. A common string processing algorithm is the global replace function found in every word processor. Write a method that takes three String arguments: a document, a target string, and a replacement string. The method should replace every occurrence of the target string in the document with the replacement string. For example, if the document is "To be or not to be, that is the question" and the target string is "be" and the replacement string is "see", the result should be "To see or not to see, that is the question.".
2. Use the StringBuffer class to solve the previous problem.
3. Use just the String class for this example. Design and write a program that searches for single-digit numbers ( 0, 1, 2, ... 9) in a string and changes them to their corresponding words. For example, the string "4 score and 7 years ago" would be converted into "four score and seven years ago."
4. Use the StringBuffer class to implement a solution to the previous problem.
5. Create your own version of a StringBuffer class that provides the following:
  - an equals method
  - the 4 indexOf routines that show up in the String class
  - A case insensitive version of indexOf
  - A fast case insensitive lookup for a specific string like "</textarea>"
  - Write a method which reverses the characters in a string. Don't use the reverse method in StringBuffer --- that's too easy.
6. Create a program that converts a String into an number. You might be aware of Integer.parseInt which accomplishes this job and wonder why write your own. Integer.parseInt is quite picky about strings with none-digits. For example, a leading space or trailing space on the string results in an error.
7. Create a program that takes a line of input that could have come from a spreadsheet where tab characters separate columns and NewLines terminate a Row. Your program should process one row and return an array of the data in the columns.
8. If you want to display Html in an html page, you need to escape replace "<" with "&lt;" and replace ">" with "&gt;". Write a program that takes an html source and provides the above transformation.
9. Extra Exercises section consider: Pack/Unpack
10. Extra Exercises section consider: Hangman
11. Extra Exercises section consider: Gathering Words from a Paragraph
12. Extra Exercises section consider: Sorting
13. Extra Exercises section consider: Binary Search

Last Updated: September 17, 2003 1:15 PM