Goal • First version of linear search Generic Programming – Input was array of int • More generic version of linear search and – Input was array of Comparable Inner classes • Can we write a still more generic version of linear search that is independent of data structure? – For example, work even with 2-D arrays of Comparable, or List, or ArrayList, etc. 1 2 Minor rewrite of linear search “Obvious” linear search code boolean linearSearch (Comparable[] a, Object v) { boolean linearSearch (Comparable[] a, Object v) { int i = 0; for (int i = 0; i < a.length; i++) while (i < a.length) { if (a[i].compareTo(v) = = 0) return true; if (a[i].compareTo(v) = = 0) return true; return false; else i++; } } Code in red relies on data being stored in a 1-D array. return false; For-loop also implicitly assumes that data is stored in 1-D array. } This code will not work if data is stored in a more general Intuitively, linear search needs to know data structure such as a 2-D array. - are there more elements to look at? - if so, get me the next element 3 4 Intuitive idea of generic linear search Key ideas in solution 4 22 22 -9 4 • Iterator interface Linear search 234 -9 • Linear search written once and for all using Iterator interface • Data class that wants to support linear • Data is contained in some object. • Object has an adapter that permits data to be enumerated in search must implement Iteratorinterface some order. • Implementing Iterator interface • Adapter has two buttons – boolean hasNext(): are there more elements to be enumerated? – We look at several approaches – Object Next(): if so, give me a new element that has not been enumerated so far 5 6 1
Generic Linear Search Iterator version Iterator interface boolean linearSearch(Iterator a, Object v) { while (a.hasNext()) if ((Comparable) a.next()).compareTo(v) = = 0) interface Iterator { return true; boolean hasNext(); return false; Object next(); } void remove(); //we will not use this } Compare with Array version This interface is predefined in Java. boolean linearSearch(Comparable[] a, Object v){ Linear search is written using this interface. int i = 0; Data class must provide an implementation of this interface. while (i < a.length) if (a[i].compareTo(v) = = 0) return true; return false; 7 } 8 Adapter (version 1) How does data class implement Iterator interface? class Crock1 implements Iterator{ private Comparable[ ] a; private int cursor = 0; //index of next element to be enumerated public Crock1(…) { …store data in array a… } Let us look at a number of solutions. public boolean hasNext( ) { return (cursor < a.length); } 1. Adapter code is part of data class public Object next( ) { 2. Taking adapter code out of the data class // may throw ArrayIndexOutOfBoundsException return a[cursor++]; 3. a – d : Putting it back in } public void remove( ) { throw new UnsupportedOperationException( ); } } 9 10 Critique • One solution to resetting the cursor: – Data class implement a method void reset() which • As shown, client class can only enumerate resets all internal cursor(s) elements once! – Method must be declared in Iterator interface – No way to reset the cursor • But we still cannot have multiple enumerations of • Making the data class implement Iterator directly elements going on at the same time is something of a crock because its concern should • Remember: only one cursor per data array… be with data, rather than enumeration of data. • Problem: cannot create new cursors on demand • However, this works for other data structures such • To solve this problem, cursor must be part of a as 2-D arrays. different class that can be instantiated any number of times for a single data object. – 2-D arrays: data class can keep two cursors • one for row • one for column • standard orders of enumeration: row-major/column-major 11 12 2
Adapter (version 2) Sharks and remoras class Shark { protected Comparable[] a; public Shark(…) {…get data into a…} Iterator implementation } Data class is like shark is like a remora. class Remora implements Iterator{ private Shark myShark; private int cursor = 0; public Remora(Shark s) { myShark = s; } Remora teeth public boolean hasNext() { // a in Shark is protected, so accessible return (cursor < myShark.a.length); } public Object next( ) { return myShark.a[cursor++]; } public void remove( ) {…} } Single shark must allow us to hook many remoras to it. 13 14 Critique Client code: Shark s = new Shark(…data…); • Good: Remora r1 = new Remora(s); – Shark class focuses on data, Remora class focuses on Remora r2 = new Remora(s); enumeration linearSearch(r1, v); • Bad: – Remora code relies on being able to access Shark variables (e.g. array a must be made protected ) myShark Shark • What if a was declared private? cursor = 3 • Protected access is less secure than private. – Remora is specialized to Shark but code appears outside Shark class myShark Remora • 2-D array Shark will require a different Remora cursor = 5 • We may change Shark class and forget to update Remora. Remora 15 16 Slightly better code: Shark object creates Remoras in request class Shark { // client code Critique protected Comparable[] a; public Shark(…) {…get data into a…} Shark s = new Shark(…data…); • Good: – Shark code mentions Remora, so person modifying Shark public Iterator makeRemora( ) { // let shark make a Remora // Shark code mentions Remora class Iteratorr1 = s.makeRemora( ); code is at least aware that Remora code depends on this return new Remora(this); class. } // or make our own • Bad: } Remora r2 = new Remora(s); – Clients can still create Remoras without invoking linearSearch(r1, v); makeRemora method class Remora implementsIterator { private SharkmyShark; • Better to have language construct to enforce such a convention … • Better to force them to use Iterator, not Remora } – Code in separate files, but closely intertwined: • Remora used in exactly one place: inside Shark.makeRemora() see iterator-2-outside files 17 18 3
Idea: Move Remora Closer An Old Idea: In Same File • Want Remora to be as close as possible to • Problem: Compiler prefers to have one class the one single place that uses it. per file • What does this mean? – How could it know where to find Remora code during compilation? – In same file? – In same class? – In same method? – On same line? 19 20 Make it Static: Nested Classes Better Idea: In Same Class class Shark { private Comparable[ ] a; // client code • Let’s make Remora class a “member” of public Iterator makeRemora( ) { return new Remora(this); Shark s = new Shark(…data…); Shark class } – Should it be static or not? // let shark make it for us public static class Remora //nested class Iteratorr2 = s.makeRemora( ); – Should it be public or not? implements Iterator{ Shark myShark; // make our own Remora int cursor = 0; Remora r1 = new Shark.Remora(); // same code, but has access // to private member myShark.a linearSearch(r2, v); public Object next( ) { return myShark.a[cursor++]; } see iterator-3a-nested files } } 21 22 Critique Make it Non-Static: Inner Classes • Good: class Shark { – Shark code and remora code in the same file private Comparable[ ] a; // client code • Class is Shark.Remora so compiler knows where to find it. public Iterator makeRemora( ) { return new Remora(); – Users know that Remora is specific to Shark class. Shark s = new Shark(…data…); } – Can now have Dolphin.Remora, Whale.Remora, etc… // let shark make it for us – Data is private again public class Remora // inner class Iteratorr2 = s.makeRemora( ); implements Iterator{ • Bad: int cursor = 0; // make our own Remora – Clients can still create Remoras on their own // same code, but “myShark” is implicit Remora r1 = s.new Remora(); public Remora( ) { } – Each Remora “belongs” to exactly one Shark public Object next( ) { • Java language could enforce this linearSearch(r2, v); return a[cursor++]; – Remora code must keep writing “ myShark.blah” } } • It could be implicit, since everything in Remora refers to the Shark see iterator-3b-inner files } 23 24 4
Recommend
More recommend