Searching Example Using Generics

This example has two main purposes:

  1. it shows how you can make use of Java's generic classes.
  2. it shows how you can use interfaces to allow for easy interchangeability of data structures

Key points about Java generics

  1. They always use <> brackets
  2. The type parameters must be classes, not primitive types. The reason that the type parameters must be classes is that Java keeps only one bytecode copy of a generic class rather than one copy for each type instantiation (e.g., one for List<Integer> and one for List<String>) as in C++. Keeping one bytecode copy works only if all generic variables represent classes since the bytecode depends on all variables being a descendent of the Object class (e.g., the bytecode assumes that all variable accesses occur through pointers, not primitive types). A generic can go further and require that its type parameters implement certain interfaces. For example, a SortedMap generic requires its key parameter to implement the Comparator interface. The Comparator interface contains a compare and equals method, and hence any implementation of SortedMap can obtain an ordering of keys by calling the compare and equals methods.

The program shown at the end of this web page solves the following problem:

Write a Java main program that determines whether or not various search terms are in a list of strings. The input will come from the command line as follows:

  1. an integer, n, that represents the number of strings in the list
  2. n strings representing the strings in the list
  3. an unspecified number of strings. For each string your program should determine if the string is in the list.

Write a search method that you call from your main program to perform the actual search. It should return true/false depending on whether or not it finds the string. Its parameters should include the collection of strings and the string to be found.

Your main program should use Java's sorted tree implementation to store the list of strings. It should then loop through the remaining strings on the command line, call the search method, and print the result using System.out.printf. Your program should print the string and either true/false. For example, the command:

   java search 3 brad nels james jim nancy brad nels
should produce the output:
   jim false
   nancy false
   brad true
   nels true

The Program

import java.util.*; class search { // The first type parameter to Map represents the type of the key and // the second type parameter represents the type of the value. In this // case String represents the key type and Integer represents the value // type static boolean find(Set<String> myCollection, String key) { return myCollection.contains(key); } public static void main(String args[]) { int n = Integer.parseInt(args[0]); // parseInt converts String to int // java.util.HashSet is Java's hash table implementation for a set Set<String> searchCollection = new HashSet<String>(); int i; // add is the method provided by Set to insert a new key for (i = 0; i < n; i++) { searchCollection.add(args[i+1]); } for (i = n+1; i < args.length; i++) { // %b is the formatting string for a boolean System.out.printf("%s %b\n", args[i], find(searchCollection, args[i])); } } }

Interchangeability of Data Structures

Since the above code declares searchCollection to be an interface type (i.e., Set), rather than a class type (i.e., HashSet), a one word change to the code transforms the program from using a hash table to using a tree. I can simply change the line that reads:

Set<String> searchCollection = new HashSet<String>(); to Set<String> searchCollection = new TreeSet<String>(); If I had instead declared searchCollection to be a specific class type, namely HashSet, then I would need to change the declaration of searchCollection in both the above line and in its parameter declaration in find. Hence by using interface types I make it easier to rapidly replace data structures if I so desire.