Module 3: Arrays of chars and strings


Objectives

 

By the end of this module, for simple programs, you will be able to:

 

3.0 Audio:
 


3.0    Arrays of chars

 

Working with a char array is similar to what we did with int arrays or double arrays.

Consider this example:


public class CharArrayExample {

    public static void main (String[] argv)
    {
	char[] word = {'a', 'l', 'o', 'h', 'a'}; 

	for (int i=0; i<word.length; i++) {
	    System.out.println (word[i]);
	}
    }

}
     
  • Here, the empty square brackets after the reserved word char indicate the declaration of an array of char's:
    	char[] word = {'a', 'l', 'o', 'h', 'a'}; 
         
  • The actual variable (our choice of variable name) is word:
    	char[] word = {'a', 'l', 'o', 'h', 'a'}; 
         
  • And, in this case, we are specifying the array contents at the time of declaration:
    	char[] word = {'a', 'l', 'o', 'h', 'a'};
         
  • We could have, alternatively, assigned each array entry:
    	char[] word = new char [5];
            word[0] = 'a';
            word[1] = 'l';
            word[2] = 'o';
            word[3] = 'h';
            word[4] = 'a';
         
  • Notice the syntax for specifying the size of an array whose elements are to be assigned later:
    	char[] word = new char [5];
         
    We are using the reserved word new here.
 

3.1 Exercise: Type up the above (in MyCharArrayExample.java) but change the "aloha" to your own greeting, or a primal scream if you prefer.
 

Let's do something more interesting in the next example:


public class CharArrayExample2 {

    public static void main (String[] argv)
    {
	char[] word = {'d', 'e', 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e'};
	char c = 'e';

	for (int i=0; i<word.length; i++) {
	    if (word[i] == c) {
		System.out.println ("Found 'e'");
	    }
	}
    }

}
     
  • Here, we are examining each entry in the array to see if equals (the == comparison) a given character.

  • Instead of defining the comparison with c separately, we could have written
    	for (int i=0; i<word.length; i++) {
    	    if (word[i] == 'e') {
    		System.out.println ("Found 'e'");
    	    }
    	}
         
 

3.2 Exercise: Type up the above (in MyCharArrayExample2.java) but change the word to "aloha" and look for 'a' instead of 'e'.
 

3.3 Exercise: In MyCharArrayExample3.java, change the word to "dereference" and, instead of printing e's, count the number of occurences of the character e.
 

Next, let's count the number of vowels in a word:


public class CharArrayExample4 {

    public static void main (String[] argv)
    {
	char[] word = {'f', 'a', 'c', 'e', 't', 'i', 'o', 'u', 's'};
	int vowelCount = 0;

	for (int i=0; i<word.length; i++) {
	    if ( (word[i] == 'a') || (word[i] == 'e') || (word[i] == 'i') ||
		 (word[i] == 'o') || (word[i] == 'u') ) {
		vowelCount ++;
	    }
	}

	System.out.println ("Number of vowel's: " + vowelCount);
    }

}
     
 

3.4 Exercise: In module3.pdf, draw a table that traces the values of the variables, showing what happens in the loop.
 

3.5 Exercise: In MyCharArrayExample4.java, we'll perform the comparison a little more smartly. Here's an outline:


public class MyCharArrayExample4 {

    public static void main (String[] argv)
    {
	char[] word = {'f', 'a', 'c', 'e', 't', 'i', 'o', 'u', 's'};
	char[] vowels = {'a', 'e', 'i', 'o', 'u'};
	int vowelCount = 0;

	for (int i=0; i<word.length; i++) {
            // Write your code here
	}

	System.out.println ("Number of vowel's: " + vowelCount);
    }

}
     
Write an inner for-loop that will compare against each vowel and update the count accordingly.
 

3.6 Video:

 


3.1    Arrays of strings

 

Let's start with an example:


public class StringArrayExample {

    public static void main (String[] argv)
    {
	String[] someStrings = {"Hello", "World!"};
	for (int i=0; i<someStrings.length; i++) {
	    System.out.println (someStrings[i]);
	}
    }

}
     
  • Once again, the square brackets following String indicate an array of that type:
    	String[] someStrings = {"Hello", "World!"};
         
  • In this case our variable name is someStrings.

  • Initialization is similar to other types of arrays. Here, of course, each array entry is an entire string, delimited by double-quotes.
    	String[] someStrings = {"Hello", "World!"};
         

  • Once again, we could done array creation and assignment separately:
    	String[] someStrings = new String [2];
            String[0] = "Hello";
            String[1] = "World!";
         
  • Notice the syntax for specifying the size of an array whose elements are to be assigned later:
    	String[] someStrings = new String [2];
         
 

Here's another example:


public class StringArrayExample2 {

    public static void main (String[] argv)
    {
	String[] someStrings = {"dereference", "engineer", "ee cummings"};
	int eCount = 0;
	for (int i=0; i<someStrings.length; i++) {
	    // The i-th string's length:
	    int L = someStrings[i].length();
	    // For the i-th string, pull out each letter to compare against 'e'
	    for (int j=0; j<L; j++) {
		char c = someStrings[i].charAt(j);
		if (c == 'e') {
		    eCount ++;
		}
	    }
	}
	System.out.println (eCount);
    }

}
     
  • The outer-loop goes through the strings one by one.

  • For each such string, we examine all the characters in the string:
    • The inner loop starts at 0.
    • And ends at the length of the current string, which we've captured in L.

  • Notice that
    		char c = someStrings[i].charAt(j);
         
    retrieves (a copy of -- it's not removed from the string) the j-th character in the current (i-th) string.

  • A slightly more compact way of writing the same code:
    	for (int i=0; i<someStrings.length; i++) {
    	    for (int j=0; j<someStrings[i].length(); j++) {
    		if (someStrings[i].charAt(j) == 'e') {
    		    eCount ++;
    		}
    	    }
    	}
         
    Here, we've dispensed with the additional variables we used earlier: L. and c.

  • What's important to notice:
    • Because someStrings[i] is the i-th entry of an array of strings, someStrings[i] is a string!
    • This means, we can go "inside the box" and use the things we've been using with strings such as
      	char c = someStrings[i].charAt(j);
           
      and
              int k = someStrings[i].length();
           
      In the latter case, for example, we are obtaining the length of the i-th string and is a shorter way than
              String s = someStrings[i];
              int k = s.length();
           
 

3.7 Exercise: In MyStringArray3.java, examine each string in an array to see if it starts and ends with the same letter, and print only those strings that start and end with the same letter. Here's an outline:


public class MyStringArray3 {

    public static void main (String[] argv)
    {
	String[] someStrings = {"alfafa", "river", "edge", "nope"};

	// Write your code here:

    }

}
     
Clearly, this should print "alfafa", "river" and "edge".
 

3.8 Audio:
 


3.2    Converting a string into an array of chars

 

Using toCharArray() in a string, one can pull out (a copy of) the letters of a string into an array:


public class StringToChars {

    public static void main (String[] argv)
    {
	String s = "Avast ye scallywags!";
	char[] letters = s.toCharArray ();
	for (int i=0; i<letters.length; i++) {
	    System.out.print (letters[i]);
	}
	System.out.println ();
    }

}
     
 

This is useful when we want to manipulate the letters. As an example, let's sort the letters in a word:

  • Note: we can't change the letters inside a string.

  • What we'll need to do: pull out the letters into an array and sort the letters within that array.

  • How to sort? We'll use this idea:
    • Find the (alphabetically) smallest letter and put that in the first spot in the array.
    • Then find the next smallest letter.
    • And so on.
 

For example:

  • Suppose we want to sort the letters
         s  h  a  l  o  m
         
  • We want to search through for the letter that should go into the first position:
         s  h  a  l  o  m
         
    and swap that with whatever is there now:
         a  h  s  l  o  m
         
  • That's done and we don't look back.

  • Next we scan from "h" onwards for the smallest.
         a  h  s  l  o  m
         
    Nothing is smaller than h, so we leave it there.

  • Now look from "s" onwards:
         a  h  s  l  o  m
         
    The smallest is "l" so swap that with "s"
         a  h  l  s  o  m
         
  • The next letter is "s" (which got moved):
         a  h  l  s  o  m
         
    and the smallest among the remaining is "m"
         a  h  l  s  o  m
         
  • After that swap:
         a  h  l  m  o  s
         
  • Then, we look from "o" onwards:
         a  h  l  m  o  s
         
    Nothing is smaller so it stays.

  • Lastly, there's "s" but nothing to its right. And so we're done:
         a  h  l  m  o  s
         
    (Remember the ahlmos?)
 

Here's sample code using a different word whose letters we wish to sort:


public class CharSort {

    public static void main (String[] argv)
    {
	// A string whose letters we want to sort:
	String w = "Cowabunga";
	// Pull the letters out into a char array:
	char[] letters = w.toCharArray ();

	for (int i=0; i<letters.length-1; i++) {

	    // Start by assuming i-th letter is the smallest.
	    char smallest = letters[i];
	    // We'll need to record the position where the smallest occurs.
	    int smallestIndex = i;

	    // Now try each position to the right of the i-th position:
	    for (int j=i+1; j<letters.length; j++) {
		// If the letter here is smaller than the smallest so far
		// then update the smallest and its location.
		if (letters[j] < smallest) {
		    smallest = letters[j];
		    smallestIndex = j;
		}
	    }

	    // We now know the smallest and where it occurs.
	    // Next: swap char at i with char at smallestIndex
	    char temp = letters[i];
	    letters[i] = letters[smallestIndex];
	    letters[smallestIndex] = temp;
	}

	// Print result.
	for (int i=0; i<letters.length; i++) {
	    System.out.print (letters[i]);
	}
	System.out.println ();
    }

}
     

This is a bit long so let's work through this in steps.
 

3.9 Exercise: Type and execute the above program.
 

Let's now explain:

  • First, the easy parts:
    	// This part pulls out the letters:
    	String w = "Cowabunga";
    	char[] letters = w.toCharArray ();
    
    	// ... the sorting part we'll explain later ... 
    
    	// Print result.
    	for (int i=0; i<letters.length; i++) {
    	    System.out.print (letters[i]);
    	}
    	System.out.println ();
         
  • OK, now let's focus on the outer loop:
    	for (int i=0; i<letters.length-1; i++) {
                // We want to get the i-th smallest letter 
                // and put that in letters[i]
    
                letters[i] = ... (somehow find the i-th smallest letter)
            }
    
         
  • The first thing to observe is that this sorts the array because:
    • The 0-th smallest letter (the smallest of all letters) will end up in letters[0].
    • The next smallest will end up in letters[1].
    • And so on.
    • That means the array will be sorted.

  • Now let's focus on getting the i-th smallest.

  • Suppose we are able to find the location of the smallest letter, then we could swap that letter into the i-th location:
    • Suppose i=2 and the location of the smallest is 5.
    • Move whatever's at location 5 into location 2.
    • Move whatever's at location 2 into location 5.
    • Now we have the smallest in location 2.

  • So, here's the swap:
    	for (int i=0; i<letters.length-1; i++) {
                // We want to get the i-th smallest letter 
                // and put that in letters[i]
    
                // Suppose the i-th smallest occurs at position smallestIndex.
    	    // We swap to get that one into letters[i].
    	    char temp = letters[i];
    	    letters[i] = letters[smallestIndex];
    	    letters[smallestIndex] = temp;
            }
    
         
  • All that's left is to find the i-th smallest.
    	for (int i=0; i<letters.length-1; i++) {
    
                // Default is what's at i:
    	    char smallest = letters[i];
    	    int smallestIndex = i;
    
    	    // Look to the right of i (from i+1 onwards)
    	    for (int j=i+1; j<letters.length; j++) {
                    // Compare with smallest so far (in the smallest variable)
    	        // and update smallest and position if needed.
                }
    
                // Do the swap etc (we've seen this) ... 
          
            }
    
         
 

3.10 Exercise: In module3.pdf, trace through the program drawing out the array at each step, as well as the values of the key variables: i, j, smallest, smallestIndex. Even though this is tedious, it will be worth it. Understanding sorting will help you understand how to work with many types of array problems.
 

3.11 Video:

 

3.12 Exercise: In StrangeComparison.java use the following template:


public class StrangeComparison {

    public static void main (String[] argv)
    {
        // Two strings:
        String w1 = "verbose";
        char[] letters1 = w1.toCharArray ();
        String w2 = "observe";
        char[] letters2 = w2.toCharArray ();

        // Sort the letters of the first string:

        // Sort the letters of the second string:

        // Now letters1 and letters2 are sorted.

        // Compare the corresponding chars in letters1
        // and letters2 to see if the two arrays are equal:

    }

}
     
What is the relationship between two words whose sorted arrays are exactly equal? And, therefore, what have we developed an algorithm to detect?
 


3.3    Fun with string arrays

 

Let's examine a list of all words from another language and perform some simple analysis.

As our example, we'll use a language that's been heard by many around the world. Here are some words in that language:

     nuqneH                 [Hello]
     HIja'                  [Yes]
     ghobe'                 [No]
     nuqDaq 'oH tach'e'     [Where's the bar?]
     
The language has many words with apostrophes.

What we'll do is count the number of words that use apostrophes:


public class KlingonCount {

    public static void main (String[] argv)
    {
	// Get the full list of words:
	String[] words = WordTool.getKlingonWords ();

	// Set up counter:
	int numWordsWithQuotes = 0;

	for (int i=0; i<words.length; i++) {

	    int quoteCount = 0;
	    for (int j=0; j<words[i].length(); j++) {
		char c = words[i].charAt (j);
		if (c == '\'') {
		    quoteCount ++;
		}
	    }

	    if (quoteCount > 0) {
		numWordsWithQuotes ++;
	    }

	}

	// Print
	System.out.println ("Total # words: " + words.length);
	System.out.println ("# with quotes: " + numWordsWithQuotes);
    }

}

     
 

3.13 Exercise: In Klingon2.java, modify the above to identify the ratio (as a decimal) of words with "u" in them to all the words. Make sure you cast the integers into double's before computing the ratio. Which vowel is used more than others in Klingon? You will need WordTool.java and klingonwords.txt.
 


3.4    A mystery demystified

 

Consider this program:


public class CommandLineArguments {

    public static void main (String[] argv)
    {
	for (int i=0; i<argv.length; i++) {
	    System.out.println (argv[i]);
	}
    }
    
}
     
 

3.14 Exercise: Type up and execute the program in the usual way:

   $ java CommandLineArguments
     
What do you see? Then, type the following at the terminal:
   $ java CommandLineArguments 1 two hello there!
     

Thus, we now understand why there was an array of strings:

  • Java wanted a way to allow users of your program to enter what are called command line arguments.

  • Think of these as options or input to provide the program.

  • These are collected together and given to you in the array of strings.
 

3.15 Exercise: At the command line, type in

   $ ls
     
Then, type in this variation:
   $ ls -1
     
The output is different (one file per line).

As you can see, ls is just another program like CommandlineArguments, which can read past the name of the program to see what additional things the user typed.

It is common for terminal (command-line) programs to feature lots of options.

Most options use a minus sign like above. This is merely Unix tradition.
 


3.5    Reading and writing

 

From here on, we will assume you will absorb whitespace conventions directly from the examples provided.

For example, you should have inferred the whitespace conventions for square brackets:

  • We write
         char temp = letters[i];
         
    but not
         char temp = letters [ i ];
         
    nor even
         char temp = letters [i] ;
         

Going forward, we will only highlight a few things regarding reading and writing that might not be apparent from the examples.
 

Strings over multiple lines:

  • It's often more readable when a long list of strings uses one line per string.

  • Thus, instead of
         String[] wordsWithNoVowels = {"glyph", "gypsy", "lymph", "myrrh", "psych", "rhythm", "syzygy"};
         
    it's often preferable to write
         String[] wordsWithNoVowels = {
             "glyph", 
             "gypsy",
             "lymph",
             "myrrh", 
             "psych", 
             "rhythm", 
             "syzygy"
         };
         
  • This allows for convenient annotation:
         String[] wordsWithNoVowels = {
             "glyph",     // sculpture term
             "gypsy",
             "lymph",     // medical term
             "myrrh",     
             "psych", 
             "rhythm", 
             "syzygy"     // astronomy term
         };
         
 

Choosing variable names:

  • This is somewhat a combination of convention (i for for-loop variable) as art, analogous to how an editor crafts section titles or article subheadings.

  • Consider some code we wrote earlier:
    	String w = "Cowabunga";
    	char[] letters = w.toCharArray ();
    
    	for (int i=0; i<letters.length-1; i++) {
    	    char smallest = letters[i];
    	    int smallestIndex = i;
    	    for (int j=i+1; j<letters.length; j++) {
       	        // ... etc 
         
  • Here, some variable names like w carry no meaning by themselves.

  • Yet, others like smallestIndex clearly do.

  • How to choose what to do?

  • Generally, workhorse variables that we're all used to, like the i's and j's in for-loops are single letter.

  • When a variable is key to understanding an idea, like smallestIndex above, it's best to be a bit verbose and make the meaning clear.

  • At the same time, we shouldn't go overboard as in: thisIsTheIndexWhereTheSmallestLetterOccurs
 


3.6    When things go wrong

 

 

3.16 Exercise: What are the two syntax errors below?

	String[] words = {"Beep", "Keep", "ee cummings"};
	for (int i=0; i<words.length(); i++) {
	    if (words[i].length > 4) {
		System.out.println (words[i]);
	    }
	}
     
 

3.17 Exercise: The program below wishes to identify words that have a letter that occurs at least twice in succession like "ee" in "beep". What is the logical error below?

	String w = "ee cummings";
	char[] letters = w.toCharArray ();
	boolean hasLetterPair = false;
	for (int i=0; i<letters.length; i++) {
	    if (letters[i] == letters[i+1]) {
		hasLetterPair = true;
	    }
	}
     
 

3.18 Audio:


On to Module 4



© 2017, Rahul Simha