Object-Oriented Programming and Data Structures - Assignment | CS 2110, Assignments of Computer Science

Material Type: Assignment; Class: Object-Oriented Programming and Data Structures; Subject: Computer Science; University: Cornell University; Term: Spring 2009;

Typology: Assignments

Pre 2010

Uploaded on 08/30/2009

koofers-user-cxi-2
koofers-user-cxi-2 🇺🇸

10 documents

1 / 42

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
CS 2110 Java structure
Classes ... making the manufacturer. It’s now time to turn our attention to
the manufacturer of all these reference objects. As an example ...
public class BankAcc {
private float balance ; // data field to hold values for each BankAcc manufactured
public float getBalance ( ) { // accessor method
return balance ; }
public void setBalance ( float bal ) { // mutator method
balance = bal ; }
public float spend ( float amt ) { // methods to do stuff
balance -= amt ; }
public float deposit ( float amt ) {
balance += amt ; }
public BankAcc ( ) { // default constructor
balance = 0.0 ; }
public BankAcc ( float amt ) { // another constructor
balance = amt ; }
} // end class BankAcc
So then, how does this get used?
1
can’t access except via methods
actually in the manufacturer
class -- it’s a visibility modifier
can access via
ANY object of
‘type’ BankAcc
but any use of
data fields grabs
only the values
for that actual
incarnation of
the object
no return type
same name as
the class name
public class GRQ {
public static void main ( String [ ] args ) {
BankAcc owen = new BankAcc ( ) ;
owen.deposit ( 5000.75 ) ;
System.out.println ( “Owen has $” + owen.getBalance ( ) ) ;
BankAcc feit = new BankAcc ( 2000.96 ) ;
feit.spend ( 3000.50 ) ;
System.out.println ( “Feit has $” + feit.getBalance ( ) ) ;
} // end main method
} // end class GRQ
owen can’t get feit’s
money, and v.v.
In general it’s a very good idea to
default to making as many of the
data fields private as possible, and
use accessor/mutator (settor/gettor)
methods to control access to them
- classes
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a

Partial preview of the text

Download Object-Oriented Programming and Data Structures - Assignment | CS 2110 and more Assignments Computer Science in PDF only on Docsity!

CS 2110 Java structure

Classes ... making the manufacturer. It’s now time to turn our attention to

the manufacturer of all these reference objects. As an example ...

public class BankAcc { private float balance ; // data field to hold values for each BankAcc manufactured public float getBalance ( ) { // accessor method return balance ; } public void setBalance ( float bal ) { // mutator method balance = bal ; } public float spend ( float amt ) { // methods to do stuff balance -= amt ; } public float deposit ( float amt ) { balance += amt ; } public BankAcc ( ) { // default constructor balance = 0.0 ; } public BankAcc ( float amt ) { // another constructor balance = amt ; } } // end class BankAcc

So then, how does this get used?

can’t access except via methods actually in the manufacturer class -- it’s a visibility modifier can access via ANY object of ‘type’ BankAcc but any use of data fields grabs only the values for that actual incarnation of the object no return type same name as the class name public class GRQ { public static void main ( String [ ] args ) { BankAcc owen = new BankAcc ( ) ; owen.deposit ( 5000.75 ) ; System.out.println ( “Owen has $” + owen.getBalance ( ) ) ; BankAcc feit = new BankAcc ( 2000.96 ) ; feit.spend ( 3000.50 ) ; System.out.println ( “Feit has $” + feit.getBalance ( ) ) ; } // end main method } // end class GRQ owen can’t get feit’s money, and v.v. In general it’s a very good idea to default to making as many of the data fields private as possible, and use accessor/mutator (settor/gettor) methods to control access to them

  • classes

CS 2110 Java structure

We can enhance the previous class ...

public class BankAcc { // static fields ‘belong’ to the manufacturer, not the made object, so only one copy of it is created, but each object can share it private static int numAccs = 0 ; // data field to hold CLASS values, initialised at compile time when the class is loaded private static int [ ] accNos = new int [ 100000 ] ; // data field to hold CLASS array for account numbers private static float totalAssets = 0.0 ; // to hold total assets of accounts, if it weren’t private then could get by BankAcc.totalAssets private float balance ; // data field to hold values for each instantiation static { // this is (surprisingly?) a method run at compile time (hence no name and no input parentheses!) for ( int i = 0 ; i < accNos.length ; i++ ) { accNos [ i ] = 100001 + i ; } // end for loop initialising account numbers } // end static compile-time method public static int getBankAssets ( ) { // this method can be called in main (for example) by BankAcc.getBankAssets ( ) ; return totalAssets ; } public float getBalance ( ) { // accessor method - perhaps should ask for authorisation? return balance ; } public void setBalance ( float balance ) { // mutator method - who should authorise this? totalAssets += balance; this.balance = balance ; } public float spend ( float amt ) { // methods to do stuff - should authenticate user? boolean bounce = ( balance < amt ) ; // bounce only exists within this method balance -= !bounce? amt : 0.0 ; if (bounce) System.out.println ( “Sorry, not enough money there at the moment (: ” ) ; else totalAssets -= amt ; } public float deposit ( float amt ) { // perhaps should offer a receipt? totalAssets += balance ; balance += amt ; } public BankAcc ( float balance ) { // another constructor totalAssets += balance ; this.balance = balance ; numAccs++ ; } // note that this gets incremented each time a new account is created! public BankAcc ( ) { // default constructor this ( 0.0 ) ; } // note the use of this to refer to one of the constructor -- has to be first statement in the constructor } // end class BankAcc

  • classes 2 local name limited to the scope of the method definition the ‘balance’ of the current object

CS 2110 Java structure

Suppose we had a Textbook class ...

public class Textbook {

}

then every time it gets invoked via TextBook FredBloggs = new TextBook ( ) ;

the particular object (reference) just created is imbued with all the

characteristics of a TextBook by this one line! This amounts to a

tremendous saving of effort on our part together with a significant

lessening of potential error. Assuming the relevant classes exist ...

public class TextBook { String author , title , publisher ; int n , isbn , cryear ; Preface pre = new Preface ( ) ; Acknow ack = new Acknow ( ) ; Contents cont = new Contents ( ) ; Chapters [ ] chaps = new Chapters [ n ] ; Index indy = new Index ( ) ; Exercises trouble = new Exercises ( ) ;


} // end class TextBook

where the ------ would have the constructors plus any useful methods (like

writeChapter ( int k ) { ...... } or makeIndex ( ) etc.).

Of course, not every book is a TextBook , we should also have classes for

Novels , Atlases , Cookbooks , Dictionarys , etc..

  • classes 4

CS 2110 Java structure

Thankfully, Java provides the mechanism of

inheritance to save us from too much

repetition in dealing with this

situation. The essential idea is

that a child inherits everything a

parent has, but can have some

things of its own. This leads to

the power of attorney rule: if in

some situation you’re expecting a parent but only have a child, then that’s ok

since a child can do everything a parent can; if however you’re expecting to

see a child but only have a parent, then that is not ok since that child might

have had properties the parent doesn’t have! Notice that in this model, no

child can have more than one parent.

The idea then is to put as much

commonality as high up in

the family tree as possible,

so that a Book would have

an author , title , publisher ,

isbn , cryear. A Sectional would have an array of Chapters called chaps, etc.

  • classes 5

CS 2110 Java structure

  • Exceptions. Bad errors cause programs (and sometimes machines!) to crash. It’s better to design our programs to catch exceptional conditions before the become fatal errors. import java.io.* ; public class PrintInt { public static void main ( String [ ] args ) { InputStreamReader isr = new InputStreamReader ( System.in ) ; BufferedReader br = new BufferedReader ( isr ) ; PrintWriter pw = new PrintWriter ( System.out , true ) ; int x ; String s ; pw. println ( “Enter an integer.” ) ; try { s = br. readLine ( ) ; x = Integer. parseInt ( s ) ; pw. println ( “The integer was ” + x ) ; } // end try block catch ( Exception e ) { pw. println ( e ) ; } // end catch block for Exception } // end main method } // end class PrintInt As indicated in the example, the try block is run. If there are no problems then the catch block is ignored. If a problem occurs then the try block terminates immediately and any exception that’s thrown by the problem line gets caught by whichever catch line matches (or includes) the type of exception generated. - exceptions 7 could generate an IOException could generate a NumberFormatException we could be more specific, but this will nab the interesting ones ... nice and general runs this section goes down here only if exceptions thrown any catches are immediate

CS 2110 Java structure

  • If our example had been reading and writing from/to files instead of the keyboard/ screen, then if any exceptions had been generated in the try block, the program would have eventually stopped whilst leaving those files open! This is a bad thing. To deal with these sorts of situations, Java provides a finally block to be used after the last catch block. This finally block will be executed whether or not any exceptions are thrown or caught, and could contain lines to close each of the files that had been opened. Essentially the control paths are ...
  • Some of the standard run-time exceptions are ...

ArithmeticException

NumberFormatException

IndexOutOfBoundsException

SecurityException

NullPointerException

NegativeArraySizeException

Some other standard checked exceptions ...

EOFException FileNotFoundException IOException

These checked exceptions must be dealt with either by try/catch blocks within the method, or by having a try/catch arrangement higher up in one of the calling programs to catch the exception coupled with an appropriate throws statement in the method declaration(s) to throw the exception “upstairs”.

  • exceptions 8

CS 2110 Java structure

Threads. So far, we have been running a single thread of control, but it’s often convenient to be

able to run either several threads independently and concurrently, or have threads branch off

and yet still communicate with one another. We’ll look at a few elementary approaches to

multi-threading (for a more in-depth discussion, follow the link to the Java Specification on the

course homepage and read ch 17) ...

There is a Thread class, so we could do ...

Thread sausage = new Thread ( ) ;

to create a new thread sausage which can be configured and run. However sausage.run( );

won’t do anything ... the computer doesn’t know anything special about running sausages!!

Better would be to extend the Thread class and then redefine run( ) in the derived class ...

public class PingPong extends Thread { // from the java.sun.com thread tutorial private String word ; // the word to print private int delay ; // the delay in millisecs to pause public PingPong ( String parole , int pendant ) { this.word = parole ; this.delay = pendant ; } // end constructor public void run ( ) { // overriding Thread’s run ( ) method try { // since sleep can throw an InterruptedException for ( ; ; ) { // never stop (unless interrupted) !!!! System.out.print ( word + “ ” ) ; sleep ( delay ) ; } } catch ( InterruptedException ie ) { return ; } } // end overriding of the run ( ) method } // end class PingPong

  • threads 10

Then if we have ...

public static void main ( String [ ] args ) { new PingPong ( “ping” , 333 ). start ( ) ; new PingPong ( “PONG” , 1000 ). start ( ) ; } // end main method

we’ll get ping appearing on the screen

every 1/3 second and PONG every

second (they’ll have fractionally different

start times) ...

ping PONG ping ping ping PONG ping ping ping PONG ping

Hence two separate and independently

running threads.

CS 2110 Java structure

There’s another more or less equivalent way of doing this, especially useful

if you want to inherit from some class and don’t want to use up your one

inheritance opportunity by having to extend the Thread class ...

public class RunPingPong implements Runnable { // from the “java programming language” book private String word ; private int delay ; public RunPingPong ( String parole , int pendant ) { this.word = parole ; this.delay = pendant ; } // end constructor public void run ( ) { try { for ( ; ; ) { System.out.print ( word + “ ” ) ; Thread. sleep ( delay ) ; } // only exit from for loop is via an interrupt } catch ( InterruptedException ie ) { return ; } } // end implementing the run ( ) method } // end class RunPingPong

We’ll say more about interfaces like Runnable in the next section. For now

you can think of them as mold-like superclasses which only have methods -

declared, but never defined. They come with an implied contract to define

every method they have if you want to implement (née extend) them. For

the case of Runnable , it only declares one method, namely run( ).

  • threads 11

Then if we have ...

public static void main ( String [ ] args ) { Runnable little , bigger ; little = new RunPingPong ( “ping” , 333 ) ; bigger = new RunPingPong ( “PONG” , 1000 ) ; new Thread ( little ). start ( ) ; new Thread ( bigger ). start ( ) ; } // end main method

we’ll get the same behaviour as before.

CS 2110 Java structure

Synchronizing per se makes no

guarantee of the order of access,

but does ensure that only one

synchronized method at a time

can have access to any data fields

addressed within that method.

(Synched methods of any given instantiated

object block each other, and synched static methods

block each other at the class level, but there is no mutual

blocking of static vs non-static methods. Note that because a child

class could potentially override a parent’s synched method, it’s the case that

in the child that method is actually synched only if it’s explicitly declared as synched in the child class.)

DANGER! Synchronizing is not a universal panacea, indeed it can be quite

devastating if used without care ........... Imagine a bunch of quick processes

waiting while a synched laborious process rambles on. Worse still, suppose

you have two instances x and y of a class G having synched methods hug( )

and hugback( ) which act on G’s, and suppose hug invokes hugback( ). Then

thread A ......... x. hug ( y ) ............................... y. hugback ( x ) ...................

thread B ............................... y. hug ( x ) .......................................x. hugback ( y ) ..................

  • threads 13 whichever of these methods is in the first thread to access balance blocks any further access to balance by either of these methods until done. Actually, the blocking is even more aggressive*. public class BankAcc { private float balance ; ............... public synchronized float spend ( float amt ) { balance - = amt ; return balance ; } public synchronized float deposit ( float amt ) { balance += amt ; return balance ; } ................ } // end class BankAcc so thread A has a lock on y so thread B has a lock on x so now A wants a lock on x but is blocked by B so now B wants a lock on y but is blocked by A this’ll be ok as soon as B is done and releases the lock OOPS!!! “deadlock” waiting waiting

CS 2110 Java structure

We can also synch whole chunks of code as a ‘local’ statement. Consider the following method

to convert an int array to absolute values ...

public static void abs ( int [ ] values ) {

synchronized ( values ) {

for ( int i = 0 ; i < values.length ; i++ )

if ( values [ i ] < 0 ) values [ i ] = - values [ i ] ;

} // end synched block on the values array

} // end abs method

(Although safe in a single-threaded environment without the ‘synchronized’ epithet, it must be

synched if multi-threaded, otherwise some other thread might access value[ i ] after the abs

method reads the boolean test, and then overwrite value[ i ] with 23, so that when abs

multiplies and writes, it will leave the array with value[ i ] = -23. Being able to synch smaller

chunks of code is a valuable option, since in general we don’t want to force other processes to

have to wait longer than necessary, plus it can help evade some potential deadlocks. In

particular this allows us to have a more fine-grained control on acquiring and releasing locks,

for instance, we could use this to allow parallel calls to multiple methods within a class yet still

protecting access to disjoint data fields.)

If you already have code written without any thought of multi-threading, rather than rework

the whole code with intricate synchs, you can create an extended class to override the

appropriate methods, declare them synchronized, and then forward method calls through the

super reference. If only occasional synchronised access is needed, then it’s usually easier just to

use a synched statement as above. (More on threads later in the course.)

  • threads 14

CS 2110 Data Structures

  • Note that promising to implement an interface forces (via the compiler) an implementation of every method listed in that interface (but doesn’t insist on the method definitions being sensible!) ... public class Q1 implements Queue { void joinQ ( Person p ) { return ; } Person leaveQ ( ) { return new Person ( “anonymous” ) ; } int getLength ( ) { return Integer. MAX_VALUE ; } boolean isFull ( ) { return true ; } boolean isEmpty ( ) { return true ; } public Q1 ( int n ) { System.out.println ( “This queue is so efficient it takes up almost no space!!!” ) ; } // constructor public Q1 ( ) { this ( -47 ) ; } // equally silly default constructor } // end interface Queue Sadly, the compiler will be perfectly happy with this! Perhaps rather better might be ... public class Q2 implements Queue { private Person [ ] storage ; private int length , front , back , size ; private final int DEFAULT_SIZE = 100 ; void joinQ ( Person p ) { if (! isFull( ) ) { storage [ back++ ] = p ; length++ ; } else System.out.println ( “Sorry, full up” ) ; } // end joinQ method Person leaveQ ( ) { Person temp ; if (! isEmpty( ) ) { temp = storage [ front++ ] ; length-- ; return temp ; } else System.out.println ( “Sorry, queue empty” ) ; return null; } // end leaveQ method int getLength ( ) { return this.length ; } boolean isFull ( ) { return ( this.back == size ) ; } boolean isEmpty ( ) { return ( length == 0 ) ; } public Q2 ( int n ) { size = n ; storage = new Person [ n ] ; length = 0 ; front = 0 ; back = 0 ; } // hopefully n > 0! public Q2 ( ) { this ( DEFAULT_SIZE ) ; } // or some such default value } // end class Q
  • interfaces 16 front back

CS 2110 Data Structures

  • Interfaces make no presumption about implementations - indeed, there can be multiple strikingly different implementations of any given interface. As further examples (cf the Weiss textbook, ch 3) ... public class Q3 implements Queue { private Person [ ] storage ; private int length , front , back , size ; private final int DEFAULT_SIZE = 100 ; void joinQ ( Person p ) { if (! isFull( ) ) { storage [ ( back++ ) % size] = p ; length++ ; } else System.out.println ( “Sorry, full up” ) ; } // end joinQ method Person leaveQ ( ) { if (! isEmpty( ) ) { Person temp = storage [ ( front++ ) % size ] ; length-- ; return temp ; } else System.out.println ( “Sorry, queue empty” ) ; return null; } // end leaveQ method int getLength ( ) { return this.length ; } boolean isFull ( ) { return ( this.length == size ) ; } boolean isEmpty ( ) { return ( length == 0 ) ; } public Q3 ( int n ) { size = n ; storage = new Person [ size ] ; length = 0 ; front = 0 ; back = 0 ; } // hopefully n > 0! public Q3 ( ) { this ( DEFAULT_SIZE ) ; } } // end class Q

The difference between the Q2 and Q3 implementations is that for Q2 we stored the data in a

linear array (wasting space as it emptied, and losing queue re-usability), whereas for Q3 the

storage was in a ‘quasi-circular’ array (allowing continual use). It’s worth noting that Q2 might

be well-suited in a situation having limited resource (eg queueing for food or for concert

tickets) whereas Q3 would be a better match for a renewable resource (eg printing or

processor access, or customer service).

  • Thinking more structurally about a queue, we could build a more elegant flavour ... - queues 17

CS 2110 Data Structures

  • A very similar structure, but which allows joining and leaving from anywhere within, is called a ‘linked list’. This could be implemented with arrays, but we’ll focus on a similar pointer-based approach, using our existing Node class, and thinking of leaving being the deletion of a Person from the ‘current’ location, and joining as interposing a person right after the current spot ... - lists 19 public class L1 implements List { private Node header , current ; private int length ; public void join ( Person p ) throws BadInsertionPoint { if ( current == null ) throw new BadInsertionPoint ( ) ; Node temp = new Node ( p , current. getNext( ) ) ; current. setNext ( temp ) ; length++ ; } // end join method public void leave ( ) { if (! isEmpty( ) && inList ( ) ) { getPrevious ( ). setNext ( current. getNext ( ) ) ; length-- ; } else System. out. println ( “Sorry, list is empty” ) ; } // end leave method private Node getPrevious ( ) { for ( Node n = header ; n != null && n. getNext ( ) != null ; n = n. getNext( ) ) if ( n. getNext ( ) == current ) return n ; return null ; } // this return is only reached if no ‘previous’ was found public void setCurrent ( Person p ) { // sets ‘current’ to point to where p is for ( current = header ; current != null ; current = current. getNext( ) ) if ( current. getData ( ). equals ( p ) ) return ; } public boolean inList ( ) { return ( current != null && current != header ) ; } public int getLength ( ) { return this. length ; } public boolean isFull ( ) { return false ; } public boolean isEmpty ( ) { return ( length == 0 ) ; } public L1 ( int n ) { header = new Node ( ) ; current = header; length = 0 ; } public L1 ( ) { this ( 0 ) ; } } // end class L public interface List { void join ( Person p ) ; // p inserted after ‘current location’ void leave ( ) ; // the current locn’s content is deleted int getLength ( ) ; // the number of people in the list boolean isFull ( ) ; boolean isEmpty ( ) ; void setCurrent ( Person p ) ; // set ‘cur loc’ to where p is } // end interface List current current temp

null null joining a linked list leaving a linked list

CS 2110 Data Structures

  • Actually this is a poor approach; having just one ‘current location’ will make it hard to sort a list (we’d need at least two ‘currents’ for that), and there’s some serious awkwardness in the way we manipulated current. Far better would be to divvy up the work between the list and the arrows pointing into the list (iterators) according to where things rightly belong, but we’ll delay doing this until we’ve introduced the other two main data structures.
  • A stack is another kind of emaciated list, this time only allowing entering and leaving from one end (the ‘top') ... - stacks 20 public interface Stack { void join ( Person p ) ; // p inserted at the top Person leave ( ) ; // the top is deleted int getLength ( ) ; // # of people in the stack boolean isFull ( ) ; boolean isEmpty ( ) ; } // end interface Stack public class S1 implements Stack { private Node top ; private int length ; public void join ( Person p ) { top = new Node ( p , isEmpty ( )? null ; top ; ) ; length++ ; } // end join method public Person leave ( ) { if (! isEmpty( ) ) { Person temp = top. getData ( ) ; top = top. getNext( ) ; length-- ; return temp ; } else System. out. println ( “Sorry, stack is empty” ) ; } // end leave method public int getLength ( ) { return this. length ; } public boolean isFull ( ) { return false ; } public boolean isEmpty ( ) { return ( length == 0 ) ; } public S1 ( int n ) { length = 0 ; } // n is irrelevant here public S1 ( ) { this ( 0 ) ; } } // end class S top null typically called push typically called pop