CS62 - Spring 2021 - Class 9

Example code in this lecture

   CommandLine
   LinkedList

Lecture notes

  • admin
       - midterm next week in lab
       - review next Tuesday
       - Darwin
       - Another assignment tomorrow (start early!)

  • command-line arguments
       - Eclipse is an IDE (interactive development environment)
          - it does a lot of things for you including syntax highlighting, autogeneration of some code, debugging, etc.
          - one of the other things it does for you is compile your code and then run it

       - Java is a compiled language
          - we first compile the code
             - this checks the syntax of your code
             - and generates a runnable version of your code (.class file in the class of Java)
          - we can then run the code once it is compiled

       - We can compile Java from the command line (e.g., Terminal)
          - cd into the directory where either the .java file exists (if no package) or the top level package exists
          - if no package:
             - javac MyFile.java

          - if package
             - javac package_name.MyFile.java

          - you can do multiple java files with *

       - Once compiled, you can run them:
          - java MyFile
          - java package_name.MyFile

       - when running a java program on the command-line, you can also pass arguments to the program (think, inputs) by adding extra information on the command-line
          - java MyFile more information

          - any text after the name of the file get passed as arguments to your main method!
             - the things are separated by a space so
                java MyFile more information

                - would pass two pieces of information

  • Look at the CommandLine class in CommandLine code
       - only a main method
          - iterates through the String[] and prints it out

       - if we run the code in Eclipse is just prints out 0

       - we can call it from the command-line, though
          $ javac CommandLine.java

          $ java CommandLine
          Length: 0

          $ java CommandLine this is some stuff
          Length: 4
          0:   this
          1:   is
          2:   some
          3:   stuff

       - You can do this in Eclipse too
          - Next to the run button, you can open the run dropdown menu
          - select "Run Configurations..."
          - under the "Arguments" tab there is a "Program arguments" field where you can enter command-line arguments

  • iterating through linked lists
       - how can we iterate through all of the values in a linked list?
          - how do we know were the end of the list is?
             - it will be a node whose next value is null
          - make a variable (it's often called "finger" and then move it down the list until we fall off of the end)

          Node finger = head;

          while (finger != null ) {
             // do something with the current node, finger
             finger = finger.next();
          }

          - do it recursively:

          void iterate(Node current){
             if( current == null ){
                return null;
             } else {
                // do something with current
                iterate(current.next());
             }
          }

  • write a method contains(E value). Returns true if the linked list has that value (based on .equals) and false otherwise
       - look at contains in LinkedList code
          - start at the head and traverse through the nodes
          - key: check if we've seen the node
          - stop when we either find it or we run off the end
          - at the end, see if we either found it or if we fell off the end of the list

       - look at recursiveContains in LinkedList code
          - recursive data structures are often conducive to recursive method
          - start at the head
          - two base cases: we're either null or we've found it
          - if neither of this, look at the next element

  • What does the singly linked list buy us?
       - add and remove at the front of the list in constant time!
       - what is the downside (no free lunch!)?
          - get and set are now linear time operations

  • what would we need to do to add to the end of the linked list?

  • look at the addLast method in the LinkedList class from LinkedList code
       - if it's empty, set the head equal to the new node
       - otherwise, traverse until we find the last node
          - the last node with have .next() == null
       - add a new node to the last node

  • how would we remove an element?
       - look at the remove method in the LinkedList class from LinkedList code
       - if its the head, just move the head along
       - if its not the head, need to find the element right before it
       - eventually, splice it out!

  • Which of these methods are fast/slow for singly linked lists?
       - add to the end (slow)
       - add to the front (fast)
       - contains (slow)
       - get (slow)
       - insert at an index
       - remove an element
       - set the value of an existing element
       - size

  • What does the singly linked list buy us?
       - add and remove at the front of the list in constant time!
       - what is the downside (no free lunch!)?
          - get and set are now linear time operations
          - size is linear time. Could we do better?
             - just keep another instance variable that is the number of elements   

  • Making it even better
       - Can you see any easy way to speed up any of these?
       - add just appends one thing on to the end of the list, it seems unfortunate that this takes linear time. How could we make it faster?
          - keep a tail reference
          - how does this help us?
             public addLast(E value){
                if( head == null ){
                   head = new Node<E>(value);
                   tail = head;
                }else{
                   tail.setNext(new Node<E>(value);
                   tail = tail.next();
                }
             }

          - what's the running time? Now it's constant!
       - what are the downsides to using a tail pointer?
          - more memory (though only marginally)
          - makes a few of the methods slightly more complicated
       - Can we get rid of it?
          - what would happen if we connected the next reference of the last element to the head of the list?

  • Doubly linked list
       - We can remove items quickly from the front of the list. What if we want to remove items from the end of the list?
          - ideally, you'd like to just splice it off
          - the problem, though, is that you need to know what the previous node is to do this, so you'd have to search through the list, which would still take linear time
       - the linked list we've seen so far is called a singly linked list since we just have a single forward link
       - in a doubly linked list our Node class has both a next reference as well as a prev reference
       - How does this help us?
          - for simplicity, we'll say it's not a circularly linked list
          - given the tail, we can "splice" out that node:
             tail = tail.prev();
             tail.setNext(null);
          - there are a few more details to deal with the case when we have < 2 nodes, but it's still a constant time operation now!
       - What if someone gives you a Node, call it d, and says that they want you delete it from the doubly linked list?
          d.prev().setNext() = d.next();
          d.next().setPrev() = d.prev();
          
          - we just splice it out
          - again, there are a few more details to deal with with respect to the head, etc., but it's now a constant time operation!
          - When could this happen?
             - delete all occurrences of a particular value
             - any time we have to iterate over the data already

  • Circularly linked lists
       - In a circularly linked list, all nodes have a next reference creating one big loop
       - Do we still need both a head and a tail reference?
          - the head reference is just tail.next()
             - note that even when there is only one element, we'll still have a loop (with just one element) and therefore tail.next() will still reference the head of the list
       - Did we lose any functionality from before?
          - no, we can still add things and remove things to the head of the list in constant time
          - and add things to the tail of the list in constant time

  • List fast vs. slow

          ArrayList   Singly LL   Singly LL (tail)   Doubly   LL

    add to end   fast      slow      fast         fast
    add to front   slow      fast      fast         fast
    contains   slow      slow      slow         slow
    get      fast      slow      slow         slow
    insert at index   slow      slow      slow         slow
    remove      slow      slow      slow         slow (fast if given node)
    remove at end   fast      slow      slow         fast
    set      fast      slow      slow         slow
    size      fast      fast      fast         fast

  • Java also has a LinkedList class that implements the List interface
       - http://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html