slanted W3C logo

CSE1020 Summary

An summary of many (but not all) of the major topics we studied in the second half of CSE1020.

1. Difference Between Primitives and References

The Java primitive types are boolean, char, byte, short, int, long, float, and double. A variable of primitive type stores a numeric value, or in the case of boolean, the value true or false.

You check two primitive values for equality using the operators == and !=:

   // x and y have the same value?
   if (x == y)
   {
      // x and y have the same value; do something
   }
   // x and y have different values?
   if (x != y)
   {
      // x and y have different values; do something
   }

.

A reference variable refers to an object in memory. The value of a reference variable is (something like) the address of the object in memory.

You almost never want to use == and != with reference variables. The operator == checks if the values of two references are the same i.e. if the two referenced objects are really the same object.

You almost always want to use equals with reference variables. equals checks if the state of the two references are the same. For example, the fractions 1/2 and 50/100 are mathematically equal; in Java, you would write:

   Fraction f = new Fraction(1, 2);
   Fraction g = new Fraction(50, 100);
   
   if (f.equals(g))
   {
      // f and g have the same state; do something
   }
   
   if (f == g)
   {
      // does not happen; f and g are different objects
   }

2. Common String Methods

The API for the String class is long, but you only need to familiarize yourself with some of the methods:

3. Sentinel-Based User Input

Sentinel-based user input continuously accepts user-input until the user types in a special string (the sentinel). For example, suppose you want to read in a sequence of strings until the user enters the string ".":

import java.io.PrintStream;
import java.util.Scanner;

public class SentinelInput
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      output.print("Enter a string (. to finish): ");
      String userInput = input.next();
      while (!userInput.equals("."))
      {
         // do something with userInput here
         
         output.print("Enter a string (. to finish): ");
         userInput = input.next();
      }
      
   }
}

You could use a for instead (see textbook Section 5.2.3).

4. User Terminated Input

User terminated input continuously accepts user-input until the user terminates the input stream by entering Ctrl-d under Linux or Ctrl-z at the beginning of a line under Windows. When the user terminates the input stream, the scanner hasNext methods will return false:

import java.io.PrintStream;
import java.util.Scanner;

public class UserTerminatedInput
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      output.print("Enter a string (Ctrl-d to finish): ");
      
      while (input.hasNext())
      {
         String userInput = input.next();
         
         // do something with userInput here
         
         output.print("Enter a string (Ctrl-d to finish): ");
      }
      
   }
}

5. Tokenizer

The StringTokenizer breaks a string into tokens (substrings) based on a set of delimiters (characters that separate tokens). The default behavior is to split a string at any sequence of whitespace characters (space, tab, newline).

import java.io.PrintStream;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Tokenizer
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      output.println("Enter a line of text:");
      output.println();
      String aLine = input.nextLine();
      output.println();
      
      StringTokenizer tok = new StringTokenizer(aLine);

      int numTokens = tok.countTokens();
      output.printf("\"%s\" has %d tokens%n%n", aLine, numTokens);
      
      for (int i = 1; i <= numTokens; i++)
      {
         output.printf("Token %d: %s%n", i, tok.nextToken());
      }
   }
}

If you want different delimiters, you just specify them in the constructor. For example, the following tokenizer will break a string at any sequence of spaces and punctuation marks (except for the apostrophe):

      StringTokenizer tok = new StringTokenizer(aLine, " \".,;:?!");

6. Aggregation

Aggregation describes the has-a relationship between two classes. A class is an aggregation if it has a reference to an object (other than a string according to the textbook).

An Investment has a reference to Stock.

A CreditCard has two references to Date.

A List<Fraction> has zero or more references to Fraction

.

.

Aggregation seems like an implementation detail; therefore, it should not be the client's concern. Unfortunately, aggregation can affect the client. For example, suppose that you had a list of fractions that you want to copy into another list. A shallow copy of the list will share the Fraction objects between the two lists:

import java.io.PrintStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
import type.lib.Fraction;

public class ShallowCopy
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      
      final int NUM_FRACTIONS = 3;
      final int MAX_VALUE = 100;
      List<Fraction> fractions = new ArrayList<Fraction>();
      
      // Fill the list with random fractions
      Random rng = new Random();
      for (int i = 0; i < NUM_FRACTIONS; i++)
      {
         fractions.add(new Fraction(rng.nextInt(MAX_VALUE),
                                    rng.nextInt(MAX_VALUE)));
      }
      output.println("Original : ");
      output.println(fractions);
      output.println();
      
      // Shallow copy the list fractions
      List<Fraction> copy = new ArrayList<Fraction>();
      for (Fraction f : fractions)
      {
         copy.add(f);
      }
      copy.add(new Fraction(0, 0));
      output.println("Copy : ");
      output.println(copy);
      output.println();
      
      // Modify the fractions in the copy
      for (Fraction f : copy)
      {
         f.setNumerator(-1);
      }
      output.println("Copy after setNumerator: ");
      output.println(copy);
      output.println();
      
      output.println("Original after setNumerator: ");
      output.println(fractions);
   }
}

The client must be aware that a shallow copy of an aggregate (the list) shares the aggregated references (the fractions). When the references are mutated through the copy, the original aggregate will see the change.

You should review your notes and the textbook Section 8.1.3 for aliasing, shallow copying, and deep copying.

7. Inheritance

Inheritance describes the is-a relationship between classes. The header:

public class RewardCard extends CreditCard

means that a RewardCard is-a CreditCard. From the client's point of view, this means that RewardCard has every public field and method (but not constructors) that CreditCard has.

Because RewardCard is-a CreditCard, you can use a RewardCard anywhere a CreditCard is needed. We say that RewardCard is substitutable for CreditCard.

import java.io.PrintStream;
import java.util.Random;
import type.lib.CreditCard;
import type.lib.RewardCard;
import type.lib.GlobalCredit;

public class Substitutable
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      
      GlobalCredit gc = new GlobalCredit();
      
      // Randomly populate the GlobalCredit collection
      // with credit and reward cards.
      final int NUM_CARDS = 10;
      Random rng = new Random();
      for (int i = 1; i <= NUM_CARDS; i++)
      {
         if (rng.nextBoolean())
         {
            // Ok, add takes a CreditCard
            gc.add(new CreditCard(i, "Some Name"));
         }
         else
         {
            // Ok, add takes a CreditCard and
            // RewardCard is substitutable for CreditCard
            gc.add(new RewardCard(i, "Another Name"));
         }
      }
   }
}

8. Polymorphism

Polymorphism means an object can appear to be a different type of object. Because RewardCard is substitutable for CreditCard, a RewardCard can appear to be a CreditCard. For example, we can charge all of the cards in the GlobalCredit collection even though some of them are really RewardCards:

import java.io.PrintStream;
import java.util.Random;
import type.lib.CreditCard;
import type.lib.RewardCard;
import type.lib.GlobalCredit;

public class Polymorphism
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      
      GlobalCredit gc = new GlobalCredit();
      
      // Randomly populate the GlobalCredit collection
      // with credit and reward cards.
      final int NUM_CARDS = 10;
      Random rng = new Random();
      for (int i = 1; i <= NUM_CARDS; i++)
      {
         if (rng.nextBoolean())
         {
            // Ok, add takes a CreditCard
            gc.add(new CreditCard(i, "Some Name"));
         }
         else
         {
            // Ok, add takes a CreditCard and
            // RewardCard is substitutable for CreditCard
            gc.add(new RewardCard(i, "Another Name"));
         }
      }
      
      // Charge $100 to every card in gc and
      // output the card
      for (CreditCard cc = gc.getFirst();
           cc != null;
           cc = gc.getNext())
      {
         cc.charge(100.00);
         output.println(cc.toString());
      }
   }
}

If you compile and run the program, you will find that the CreditCards and RewardCards behaved differently, even though the same methods (charge and toString) were used. The Java Virtual Machine is able to determine the proper version of these methods based on the actual type of the object that cc refers to (either a CreditCard or a RewardCard).

9. Differences Between List, Set, and Map


A list supports indexed access to its elements, and the client can control the ordering of elements in a list. The default choice for a list is ArrayList.

A set enforces uniqueness of its elements, and the client cannot directly modify the ordering of elements in a set. The default choice for a set is HashSet unless the elements of the set must be sorted, in which case the choice is TreeSet.

Lists and sets both implement the Collection interface.

.

A map uses a key to store and retrieve a value (you can think of a map as storing the pair key-value). The keys must be unique (they form a set), but the values can be duplicated (they form a collection). The default choice for a map is HashMap unless the keys of the map must be sorted, in which case the choice is TreeMap.

Maps do not implement the Collection interface.

10. Traversal (Iteration)

There are two methods for traversing the elements of any collection.

The easiest way to traverse a collection is to use a for-each loop. You can use this approach if you want to traverse only one collection, and if you do not have to remove any elements from the collection.

The following program reads a line of text from the user, separates the line into words using a tokenizer, and adds the words to a collection. It then traverses the collection, printing the number of letters in each word and each word.

import java.io.PrintStream;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.Collection;
import java.util.ArrayList;

public class ForEachLoop
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);

      Collection<String> coll = new ArrayList<String>();
      
      output.print("Enter a line of text: ");
      String aLine = input.nextLine();
      
      StringTokenizer tok = new StringTokenizer(aLine);
      while (tok.hasMoreTokens())
      {
         coll.add(tok.nextToken());
      }
      
      // for each String s in coll
      for (String s : coll)
      {
         output.printf("%2d letters : %s%n", s.length(), s);
      }
   }
}

.

If you need to traverse more than one collection at the same time, access more than one element in the loop, or remove an element from the collection, you must use iterators.

The following program fills a list with 10 random integers between 0 and 100, and computes the average. It then removes all of the elements that are less than the average value from the list using an iterator-based traversal.

import java.io.PrintStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

public class IteratorLoop
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      
      // Create list of random numbers with
      // values between 0 and 100 inclusive
      Random rng = new Random();
      final int NUM_ELEMENTS = 10;
      final int MAX_NUMBER = 100;
      double average = 0.0;
      List<Integer> numbers = new ArrayList<Integer>();
      for (int i = 0; i < NUM_ELEMENTS; i++)
      {
         int num = rng.nextInt(MAX_NUMBER + 1);
         numbers.add(num);
         average += num;
      }
      average /= NUM_ELEMENTS;
      output.println("Values    : " + numbers.toString());
      output.println("Avg value : " + average);
      
      // remove all elements smaller than the average
      Iterator<Integer> iter = numbers.iterator();
      while (iter.hasNext())
      {
         int num = iter.next();
         if (num < average)
         {
            iter.remove();
         }
      }
      output.println("New values: " + numbers.toString());
   }
}

.

You cannot traverse a map, because its interface does not support traversal. Instead, you must ask the map for its set of keys, and traverse the keys to retrieve the values:

The following program reads in a text file and counts the number of times each unique word appears in the text file. It does so by using each word as a key in a map. It then outputs each word and its word count by traversing the key set of the map and retrieving the word count corresponding to each word.

The text file used in this example.

import java.io.*;
import java.util.Scanner;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;

public class WordCount
{
   public static void main(String[] args) throws IOException
   {
      Map<String, Integer> wordCount =
            new HashMap<String, Integer>();
      
      Scanner fInput = new Scanner(new File("hogfather.txt"));
      
      while (fInput.hasNext())
      {
         String word = fInput.next().toLowerCase();
         
         // remove punctuation
         // (except for hyphens and apostrophes)
         String regex = "[^a-z-']";
         word = word.replaceAll(regex, "");
         
         // is word already in the map?
         Integer count = wordCount.get(word);
         if (count == null)
         {
            wordCount.put(word, 1);
         }
         else
         {
            wordCount.put(word, count + 1);
         }
      }
      
      // get the set of words in the map
      Set<String> words = wordCount.keySet();
      
      // for each String word in words
      for (String word : words)
      {
         System.out.printf("%-15s", word);
         Integer count = wordCount.get(word);
         System.out.println(" " + count);
      }
   }
}