CS210 Tuesday, Feb. 28

pa2 extension: submit by noon tomorrow for full credit.  Second collection Saturday noon for 5 points off.  Deadline.

Sorting and Searching Arrays—Weiss Sec. 6.4

If you have a Collection, then it’s easy to put its contents into an array, and then use Arrays.sort() to sort it.  See the Collection interface again on pg. 92, and its toArray() method.  Thus we know that any Collection can be called upon to deliver an array holding its contents.  The element objects are not copied, just their refs.  The type of the array is array of Object, very generic.

The Arrays class provides sort functions partially described on pg. 218, 220:

void sort(Object [] arr);  // for use in the case that the element class implements Comparable and that compare is what we want

void sort(Object [] arr, Comparator cmp);  // for use in other cases.  Here we say how to compare elements via the Comparator object

But in the actual Arrays class, there are many more versions, to cover the various arrays of primitive types, for example:

void sort(int [] arr); 

Example with int array:

int [] a = {1, 4, -1, 10};  // This is a sequence of ints in memory

Arrays.sort(a);   // this rewrites the array a to have contents {-1, 1, 4, 10}. 

Similarly we can sort any kind of array of primitive by calling Arrays.sort()  directly.  

Another important case is array of String.  Then we can use the first version above since an array of String IS-A Array of Object.:   

       String arr1[] = {"hi", "there", "bye"};

    Arrays.sort(arr1);

After this, arr1[0] = "bye", arr1[1] = "hi", and arr1[2] = "there".

This sort works fine as long as the actual element objects implement Comparable.  Here the element type is String, which implements Comparable.  Its compareTo() sorts strings by the Unicode character order, which agrees with the ASCII order if you only use ASCII characters, i.e., lower and upper case letters, digits, ordinary punctuation, etc.  All uppercase letters precede all lowercase ones, so “a” follows “Z”.  All lowercase letters are in alphabetical order and so are all uppercase letters.

If you want to sort strings so as to ignore the case of letters, as is done in a phone book for names, you would need to use a Comparator object with sort. 

To sort an array of ItemInfo by name, we would need a Comparator.

Binary Search in the Arrays class

Binary Search, like sort, has various forms for various types of arrays:

public static int binarySearch(Object [] arr, Object x);  // assuming elements implement Comparable

public static int binarySearch(int [] arr, int x); // and so on…


The first form can be used to locate a string in an array of Strings:

        int spot = binarySearch(arr1, "hi");

The second form can be used to locate an int in an array of ints:

       int spot = binarySearch(a, 4);

The int return value gives the position of x in the array, if it’s non-negative.  If it’s negative, its negative value tells where x fits in the array, i.e. x falls between a[p-1] and a[p] for some p.  That p determines the negative value returned by binarySearch by the rule:  value = - (p+1).  For example, if binarySearch returns -3, it means that x falls between a[1] and a[2], since -3 = - (2+1).  If it returns -2, x falls between a[0] and a{1}.  If it returns -1, x falls below a[0], the lowest possible category.

What’s this funny feature good for?  Suppose we have a sorted array and we want to add one more number and maintain the sort.  binarySearch will tell us exactly where to put the new value.  Of course we would have to shift down all the array elements to the right of that spot to make room.

For the above int array a = {-1, 1, 4, 10}, binarySeach(a, 4) returns 2, an exact match, since a[2] = 4.  binarySearch(a, 10) returns 3.  binaraySearch(9) returns -4, saying that 9 is between a[2] and a[3].

Comparators.

We have seen that many sorts and searches can be done via "natural order" expressed by Comparable.  When we need to set up some other ordering, we need to use a Comparator, an object that knows how to do a particular kind of comparison for a particular type of element.

For example, Comparator<String> caselessComparator 

public class CaselessStringComparator implements Comparator<String> {
    int compare(String lhs, String rhs)
    {
         return(lhs.toLowerCase().compareTo(rhs.toLowerCase()));
    }
}

Actually we can get this comparator from String:

String.CASE_INSENSITIVE_ORDER  IS-A Comparator<String>    a static field of String

Then you can use

void sort(Object [] arr, Comparator cmp); 

sort(arr1, new CaselessStringComparator());

sort(arr1, String.CASE_INSENSITIVE_ORDER);

Comparators can be used in binarySearch--

public static <T> int binarySearch(T[] arr, T x, Comparator<? super T> cmp);

int spot = binarySearch(arr1, "hi", String.CASE_INSENSITIVE_ORDER);

(of course this assumes we sorted it without case)

Also in TreeSet (and TreeMap) creation:

Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

We can define Comparators for our object types, but let's wait until we need it.

Section 6.6 Stacks and Queues


pa3 will involve Stacks, so we need to dig into this.

Stack idea: think of the stack of plates in old diners that were held in a spring-loaded canister so that only the top plate showed. When you took off one plate another popped up into place, ready to be taken.  You could put another in, and the pile would sink a little so that the new one was now on at the normal top.

Similarly, a "pure" Stack data structure holds elements, but only gives access to the last one in, the element at “top of stack.”  When we push another element in, it becomes the new top element, and the rest are hidden inside the stack.  We can pop the stack, removing one element, making the next element the top element.

Thus the main operations of classical academic Stack are push(Object), Object top(), and  pop(), as shown on pg. 226.  There are two versions of pop extant in the CS world, one which returns the top object, and one that doesn’t.  In both cases pop removes (pops off) the old top element.

Clearly we can use an interface to describe a Stack, and Weiss does this on pg. 226, using the version of pop() that returns void.  This is his own interface, not a Java standard.  However it captures a classic stack setup, which we can call a pure stack, as opposed to some practical setup such as we might find in the JDK. 

 Note that even this pure Stack of pg. 226 has three more operations, topAndPop(), isEmpty() and makeEmpty().  It does not have common Collection methods such as size() or iterator().  That’s because the elements under the top are supposed to be hidden.  An iterator would let us find them all, and size() would tell us something about them.

Clearly we can make a Stack from a List, and you’ll do this in pa3.  A stack is a specialized list.  Why bother with it then?

Because it provides an important mental model for doing certain calculations, as we will see.  When you know a stack is in use, you know what operations are going to be used, and that most of the elements are just going to hide away out of sight for a while until they come back into play when they get to the top.

ReverseLines Example done with a Stack

As a first example, consider a ReverseLines program that read lines from a file and output them in reverse order.  We can implement this with a stack of String.  

Picture of a stack of three lines after 3 pushes, only last line shows on top.

Top gives last line, output it, pop the stack

Now the next-to-last (middle of the three) line is visible at the top of stack.

Top gives middle line, output it, pop the stack

Now the first line is visible at the top of stack.

Top gives first line, output it, pop the stack.

Now the stack is empty, and we're done.