open MemoDatabase.java save as ArrayMemoDatabase.java minimal compile: constructor + return false/null correct -- for a limit of 0 memos demo with tests in interaction pane - need to compile MemoAssociation first - ArrayMemoDatabase m = new ArrayMemoDatabase() m.insert(new MemoAssociation("Eugene", "Kurt Vonnegut")) .. .. .. need an array -- draw a picture - CHOICE: - one array - two arrays (why have to synchronize?) - one 2-D array (complex; *less readable*) KEEP THINGS SIMPLE. Do something complex only if you must. This may not work under time pressure, so don't get in time pressure. add IVs - CHOICE: names. singular array?! "counter"? private MemoAssociation[] pair; private int counter; initialize IVs pair = new MemoAssociation[10]; counter = 0; - TRAP: don't declare local variables of the same name! retest insert -- draw a picture - special cases: full, key already there.. defer! - TO DO: handle case of array too full - TO DO: handle case of key already used simple! counter tells me next open slot pair[counter] = m; counter++; return true; retest new other methods are wrong! fix. - CHOICE: which first? containsKey seems more 'basic': fxn used/dup'ed by others containsKey -- look at picture - need loop, up to counter - NOTE: loop index is declared local to the loop.. standard. add code for (int i = 0; i < counter; i++) if (key.equals( pair[i].key() )) return true; - CHOICE: block with local var for pair's key? okay for readability - NOTE: default case is main-line. success happens in loop early. retest - now have to test both cases : SUCCESS and FAILURE - there will be more! I don't want to have to remember every time - "regression testing" * migrate from Interactions to main() public class ArrayDatabaseTest { public static void main(String[] args) {} } - copy & paste. add println's. ArrayMemoDatabase m = new ArrayMemoDatabase(); MemoAssociation eugene = new MemoAssociation("Eugene", "Kurt Vonnegut"); System.out.println( m.insert(eugene) ); // true System.out.println( m.containsKey("Eugene") ); // true System.out.println( m.containsKey("Kurt") ); // false - add answers in comments -- (is there a better way?) fix more methods - CHOICE: which next? find() seems natural -- same idea, different value! find -- look at picture - copy & paste. change return for (int i = 0; i < counter; i++) if (key.equals( pair[i].key() )) return pair[i].value(); retest - add tests for both cases System.out.println( m.find("Eugene") ); // "Kurt Vonnegut" System.out.println( m.find("Kurt") ); // null - now a good time to test second insert! find should succeed. MemoAssociation kurt = new MemoAssociation("Kurt", "Joseph Heller"); System.out.println( m.insert(kurt) ); // true System.out.println( m.containsKey("Kurt") ); // true System.out.println( m.find("Kurt") ); // "Joseph Heller" System.out.println( m.find("Eugene") ); // "Kurt Vonnegut" remove -- look at picture - CHOICE: what do I do with the empty slot?? - could leave null in slot, but then every loop must - check null before doing anything - search to end of allocated arry - better to fix it -- find and containsKey depend on sequential items - CHOICE: copy last item in, or move all items down? order is not essential for this program so: move then at the end --- and don't forget to decrement! - copy & paste the loop, but make major changes if (key.equals( pair[i].key() )) { pair[i] = pair[counter-1]; counter--; return true; } - special cases? - empty? -- not a problem -- the loop never runs, never does anything - last item? -- not a problem -- just unnecessary copy retest - add tests for both cases System.out.println( m.remove("Eugene") ); // true System.out.println( m.find("Eugene") ); // false System.out.println( m.find("Kurt") ); // "Joseph Heller" - are we still sure all the previous cases still work? return to insert's special cases -- look at picture case: key already used - easy -- check containsKey, as DefaultDB did if ( containsKey(m.key()) ) return false; - NOTE: normal case is main-line. special case exits early. retest MemoAssociation eugene = new MemoAssociation("Eugene", "Kurt Vonnegut"); MemoAssociation duplicate = new MemoAssociation("Eugene", "Franz Kafka"); System.out.println( m.insert(eugene) ); // true System.out.println( m.insert(duplicate) ); // false System.out.println( m.find("Eugene") ); // "Kurt Vonnegut" case: array full - CHOICE: what to do?? grow or reject .. simplest: reject (precedent) - ISSUE : return false leads to mistaken message to user - ideal: fix it (but false can't mean both things..) - simple: leave it be. [ I allowed this ] if ( counter == pair.length ) return false; - COMMENT THIS!! // // Returns false if array is full.. .. Ideally.. .. // retest // test full array m = new ArrayMemoDatabase(); for (int i=0; i < 10; i++) m.insert( new MemoAssociation("key"+i, "value"+i) ); System.out.println( m.insert(eugene) ); // false and for good measure System.out.println( m.find("Eugene") ); // null System.out.println( m.find("key9") ); // "value9" - are we still sure all the previous cases still work? NOTICE: main() is a pretty good test program for grading your solutions!! ----- FINALLY: Task 2 public class MemoPad extends CloseableFrame ... database = new ArrayMemoDatabase(); // CHANGE THIS LINE compile MemoPad, MemoPadApp run.. .. .. harder to test full case! ----- NOTICE duplication for (int i = 0; i < counter; i++) if (key.equals( pair[i].key() )) problems.. .. .. - if we have to change one copy, we often have to change all - error trap! - makes code harder to read, understand the loops don't say "why" -- plain Java can't - if we had used DefaultDB as model, we would have inefficiency - find() and remove() call containsKey() -- multiple passes! solution - "factor out" common code or common idea - (save a copy of ArrayMemoDatabase.java -- it works!!) - start with containsKey: call a helper that returns the slot for (int i = 0; i < counter; i++) if (key.equals( pair[i].key() )) return i; - CHOICE: what do I return if not found? - has to be an int, has to be invalid subscript - Java idiom: -1 as flag for index return -1; (compile) now use slotFor() in containsKey() int location = slotFor( key ); if (location >= 0) return true; return false; (run tests) even better: no need for "if x = true then true else false" int location = slotFor( key ); return (location >= 0); or even return slotFor( key ) >= 0; now use slotFor() in find() int location = slotFor( key ); if ( location == -1 ) return null; return pair[location].value(); and use slotFor() in remove() ----- do extra credit (have time for code?) - draw picture - now: order matters - but: we don't have to sort! we insert one at time -- keep in order - insert: - helpers modify slotFor() to return slot hold/should-hold? can use binary search write insertAt() to move all items right one slot - remove: order *is* essential for this program - so copy all to right back one slot - containsKey and remove can use binary search, too - advantages of helper factored out!! -- all in one place -- maybe already done done insert misc notes - hardcoded constant 10 - as constant - as method!? - accepting memos when the array is full: grow - make a new, larger array, copy all, then add new - test cases - as helper methods? - create a test object? - issue: duplication -> set-up - issue: have to examine output manually (with many, now a problem) - design decisions ... - you have choices. keep things simple until you feel comfortable. * you must satisfy the spec (implement the interface) you can always ask.. .. .. - Dr. Java versus Eclipse