Course Overview We started class with a clip from the movie "The Paper Chase". I recommend that you rent this movie and watch it. I will play about a half a dozen other clips from this movie at the start of the upcoming classes, as we follow Mr. Hart through his first year of law school, as he tangles with Professor Kingsfield. I then briefly examined the overview document and toured through various other documents on the course web site, mostly reachable from the left (yellow) index. I recommend that you continue to explore the course web site: look especially closely at the Syllabus document. We looked a bit at the details about the textbook and at all the testing instruments, and how the final grade will be computed. I commented that students who come to question the grading of their quizzes, programs, and exams set up a win-win situation. Either I did grade it wrong and you deserve more points (a win), or you will learn what was wrong with a solution that you thought was correct (another win). Please read the "Academic Integrity Contract", which you should sign and return during the next lecture. Note that if you want me to use the last 5 digits of your UCI ID as your key in the gradebook, please leave this information BLANK. If you want me to use a different number, write that number on your returned Academic Integrity Contract. In the past almost all the students use the last 5 digits of your UCI ID, which I have access to from the course roster). Next we talked about the three major goals for this class. The focus of ICS 23 is (abstract) data types, data structures that implement them, and analyzing the performance of these implementations: their time and space efficiency. There are many practical and theoretical issues related to these goals. 1) Demonstrate skill solving problems/programming with Java collection classes: specifically, understand the operations that we can perform on the (abstract) data types Stack, Queue, PriorityQueue, List, Set, Map, Equivalence Class, and Graph (and their iterators); and use combinations of these data types and their operations to solve problems. We call these collections data TYPES or ABSTRACT data TYPES because they are characterized completely by their external behavior. E.g., in queues we know that if Java executes q.add("a"); followed by q.add("b"); and q.add("c"); then calling q.remove() will returrn "a" and leave only "b" (first) and "c" (last) in the queue, no matter how such a queue is actually implemented in Java. Likewise, in stacks we know that if Java executes q.add("a"); followed by q.add("b"); and q.add("c"); then calling q.remove() will returrn "c" and leave onlyl "b" (at the top) and "a" (at the bottom) in the stack, no matter how such a stack is actually implemented in Java. We use interfaces in Java to specify the operations that we can perform on a data type. 2) Demonstrate skill at using low-level Java data structures (primarily arrays and self-referential/linked data structures) to implement these data types correctly (how the information is represented and what algorithms process the information) and ensure that they run efficiently. There are many different data STRUCTURES that we can use to implement every data TYPE. E.g., there are various implementations of queues that use arrays and linked data structures, each correctly producing the required queue behavior. I will provide slow implementations for all these data types, which use simple array data structures to implement their behavior. Using these simple implementations, we can write programs (using these data types) that execute correctly, if slowly. During the quarter we will discuss more interesting data structures and use them to re-implement these data types more efficiently. By substituting a faster implementation for a data type (a few edits in our code) our programs will run more quickly. We use classes (often written using inheritance) in Java to specify the concrete implementation of these data types using the data structures that we will learn. 3) Understand big-O (and big-Omega and big-Theta) notation, and demonstrate the ability to use these notations to analyze the efficiency of implementations of collection classes, and understand the appropriate use and limits of these notations (and compare and contrast them to collecting empirical data). Since all data structures that implement a data type have the same external behavior (a program using one should produce an equivalent result to a program using another), their primary difference is how fast their operations execute (and to a lesser extent, how much memory they occupy). We will use mathematical and empirical approaches to study the efficency of the algorithms that run on various data structures. In pursuit of goals 1-2, we will gain experience using intermediate-level Java programming ideas: interfaces, abstract classes, inheritance, recursion, hashing, the decorator pattern, etc. I showed how we will draw object/instance variables to illustrate data structures. Basically, every OBJECT is always represented by an OVAL, labelled with the class from which the object was constructed; inside an object are its INSTANCE VARIABLEs, each represented by a RECTANGLE labelled with the type and name of the instance variable, and its value (often a reference) inside the rectangle. I will also illustrate how array objects are drawn using this convention. Here are the declarations that I used (simplified a bit) public class ArrayStack { //Instance variables private Object[] stack; private int objectCount = 0; //Constructor public ArrayStack (int initialSize) { ... stack = new Object[initialSize]; } //...other methods } to show a picture for the reference variable s, given ArrayStack s = new ArrayStack(4); s.push("A"); s.push("B"); Finally, we will review the Interfaces reading (time permitting, and in the first discussion secton). We will use interfaces quite a bit during the quarter. You don't have to master them now, but you really should try to work through this material. Interfaces are the main topic of the first quiz; typically quizzes are handed out in classs on Friday and are due the following Monday. Problem #5: First, here is the OriginDistance class. ------------------------------------------------------------------------------ import java.util.Comparator; import java.awt.Point; public class OriginDistance implements Comparator { public int compare(Object o1, Object o2) { Point p1 = (Point)o1; Point p2 = (Point)o2; //Compute square of distance: points have public x and y fields Double d1 = new Double(p1.x*p1.x + p1.y*p1.y); Double d2 = new Double(p2.x*p2.x + p2.y*p2.y); //Use the compareTo method in the Double class: comparing the square of // distance the same as comparing the distance return d1.compareTo(d2); } } If we used double instead of Double, we might write something like double d1 = p1.x*p1.x + p1.y*p1.y; double d2 = p2.x*p2.x + p2.y*p2.y; if (d1 < d2) return -1; else if (d1 > d2) return +1; else return 0; We can more easily write this class using generic types, saying it implements Comparator. This approach simplifies the parameter structure and avoids requiring us to cast the Object parameters explicitly. If we write code that uses the wrong type of references to compare, the result will be a compilation error rather than an class-cast exception thrown at runtime: compilation errors are preferable because they are easier to debug. You should know this form of using generics from ICS-22 or the equivalent course. public class OriginDistance implements Comparator { public int compare(Point p1, Point p2) { //Compute/Compare square of distance Double d1 = new Double(p1.x*p1.x + p1.y*p1.y); Double d2 = new Double(p2.x*p2.x + p2.y*p2.y); //Use the compareTo method in the Double class: comparing the square of // distance the same as comparing the distance return d1.compareTo(d2); } } Note that instead of using the Double wrapper class (and its .compareTo method) we could rewrite this method as public int compare(Point p1, Point p2) { double d1 = p1.x*p1.x + p1.y*p1.y; double d2 = p2.x*p2.x + p2.y*p2.y; if (d1d2) return +1; else /*if (d1==d2)*/ return 0; } ------------------------------------------------------------------------------ Here is a more general version, where we can supply the point of reference (it isn't always the origin) to the constructor for this class. ------------------------------------------------------------------------------ import java.util.Comparator; import java.awt.Point; public class Distance implements Comparator { private Point ref; public Distance(Point reference) {ref = reference;} public int compare(Point p1, Point p2) { //Compute square of distance Double d1= new Double((p1.x-ref.x)*(p1.x-ref.x)+(p1.y-ref.y)*(p1.y-ref.y)); Double d2= new Double((p2.x-ref.x)*(p2.x-ref.x)+(p2.y-ref.y)*(p2.y-ref.y)); //Use the compareTo method in the Double class: comparing the square of // distance the same as comparing the distance return d1.compareTo(d2); } } Problem #9: We cannot write a constructor for an anonymous class because constructors have the same name as the class, and if the class is anonymous it has no name. So we cannot name the constructor. But, that doesn't mean that we cannot specify values for the instance variables of the object created from that class: we can do that when we declare the instance variables in the anonymous class itself. For example, we can write int x = AdvancedPrompt.forInt ("Enter 1..5", new DecisionInt() { //Here is the anonymous class public boolean isOK(int x) // which implement DecisionInt {return low <= x && x <= high;} // by specifying the isOK method // using instance variables private int low = 1; // initialized as shown when declared. private int high = 5;; }, "..not in range [1..5]" ); In fact, since these instance variables are used just once, in IsOK, we could more easily get rid of them all together, and write just int x = AdvancedPrompt.forInt ("Enter 1..5", new DecisionInt() { public boolean isOK(int x) {return 1 <= x && x <= 5;} } "..not in range [1..5]" ); Problem #10: NegateADecision is written much as NegateAComparator is written. The real decision is saved in an instance variable, and used it isOK, with its returned value being negated. public class NegateADecision implements DecisionInt { public NegateADecsision(DecisionInt d) {realDecision = d;} public boolean isOK (int x) {return ! realDecision.isOK(x);} private final DecisionInt realDecision; } ------------------------------------------------------------------------------ I will not have enough time to show a Compose class, that composes two Univariate objects such that if h = new Compose(f,g), then h(x) evaluates (Compose implements Univariate) to the same value as f(g(x)). The code below (copy and paste it into Eclipse) not only defines this class, but includes a "main" method that tests composing f and g (and g and f) for two simple Univariate functions. You must put the Univariate interface (defined in the lecture) in a project in its own .java file along with Compose.java below. ------------------------------------------------------------------------------ import edu.uci.ics.pattis.introlib.Prompt; public class Compose implements Univariate { private Univariate f,g; public Compose (Univariate f, Univariate g) { this.f = f; this.g = g; } public double evaluate (double x) {return f.evaluate(g.evaluate(x));} public static void main (String[] args) { //Uses an anonymous class for the object implementing the interface Univariate f = new Univariate () { public double evaluate (double x) {return x + 1;} }; //Uses an anonymous class for the object implementing the interface Univariate g = new Univariate () { public double evaluate (double x) {return 2*x;} }; Univariate h1 = new Compose(f,g); //computes f(g(x)) Univariate h2 = new Compose(g,f); //computes g(f(x)) for (;;) { double x = Prompt.forDouble("Enter x"); System.out.println(" h1(x) = " + h1.evaluate(x)); System.out.println(" h2(x) = " + h2.evaluate(x)); } } } ------------------------------------------------------------------------------ Note the use of the two anonymous classes (for the functions f(x) = x+1 and g(x) = 2*x) and the use of my Prompt class (it is filled with static methods whose names determine what type of value is being prompted for). You should develop a low threshold for trying out small code snippets in Eclipse/Java to test out your knowledge. Really do try to cut/paste/run the class above in Eclipse. The more often you do this, the easier it is to do in the future.