Object-Oriented Programming in Java -- Files, Streams, and Data Structures

Files and Streams

Java applets generally prevented from reading, writing files for security reasons

File processing generally done by Java applications

Field = int, float, string

Record = several related fields

File = several related records

Database = several related files

Record usually has unique field for storing and retrieval purposes = record key

Sequential file = records stored in order by key

Files and Streams

Java views file as sequential stream of bytes (usually terminated by end-of-file marker)

Opening file = object created, stream associated with object

Java applications have automatic files (stream objects):

System.in (standard input stream object) usually keyboard

System.out (standard output stream object) usually screen

System.err (standard error stream object) usually screen

Java file processing requires

import java.io.*;

includes classes:

FileInputStream, FileOutputStream

PrintStream (performs output to screen)

DataInputStream (reads data via readDouble, readFloat, readInt, readUTF (characters), etc.)

DataOutputStream (writes data via writeDouble, writeFloat, writeInt, writeUTF, etc.)

Chaining = typically chain DataInputStream to FileInputStream, chain DataOutputStream to FileOutputStream

Buffering = logical input/output operations performed on buffer in memory; when buffer empty/full, physical read/write performed

Buffering more efficient than physical read/write for every logical input/output

RandomAccessFile = used for random (direct) access operations to input/output to any record in a file

Writing Sequential Access File

import java.io.*; ... DataOutputStream output; ... try { output = new DataOutputStream (new FileOutputStream ("accounts.txt")); } catch (IOException ioe) { System.err.println ( "File did not open properly" + ioe.toString()); System.exit (1); } ... try { output.writeInt(int); output.writeUTF(String); output.writeFloat(float); } catch (IOException ioe) { System.err.println ( "Error writing to file" + ioe.toString()); System.exit (1); } ... try { output.close(); } catch (IOException ioe) { System.err.println ( "File did not close properly" + ioe.toString()); System.exit (1); }

When open accounts.txt...

If accounts.txt already exists, file truncated

If accounts.txt does not exist, file created

System.exit(n), n=0 means normal exit, anything else means termination due to error

Good programming practice:
output.close();
But, if you forget, will be closed when program ends.

Reading Sequential Access File

import java.io.*; ... DataInputStream input; ... try { input = new DataInputStream (new FileInputStream ("accounts.txt")); } catch (IOException ioe) { System.err.println ( "File did not open properly" + ioe.toString()); System.exit (1); } ... try { int=input.readInt(); String=input.readUTF(); float=input.readFloat(); } catch (EOFException eof) { closeFile(); } catch (IOException ioe) { System.err.println ( "Error reading from file" + ioe.toString()); System.exit (1); } ... try { input.close(); } catch (IOException ioe) { System.err.println ( "File did not close properly" + ioe.toString()); System.exit (1); }

Attempt to read past end-of-file marker throws EOFException

Writing and Reading Random Access File

RandomAccessFile objects have all capabilities of DataInputStream and DataOutputStream objects

data read/written at location specified by file pointer

always read data in exactly same way as it was written

seek(n), re-position file pointer to byte n of the file, n must be long integer

In code below assume that accounts.txt contains records of int account (4 bytes), String name (12 2-byte characters), float balance (4 bytes)

Each record is 32 bytes

Assume accounts 1-1000 are valid account numbers

import java.io.*; ... RandomAccessFile randy; ... try { randy = new RandomAccessFile ("accounts.txt", "rw"); } catch (IOException ioe) {} ... try { if ((account >= 1) && (account <= 1000)) { randy.seek((long)(account-1)*32)); randy.writeInt(account); StringBuffer buf; buf = new StringBuffer (name); buf.setLength(12); randy.writeChars(buf.toString()); randy.writeFloat(balance); } } catch (IOException ioe) {} ... try { if ((account >= 1) && (account <= 1000)) { randy.seek((long)(account-1)*32)); account=randy.readInt(); char stuff[] = new char [12]; for (i=0; i<12; i++) { stuff[i]=randy.readChar(); } name=new String (stuff); balance=randy.readFloat(); } } catch (EOFException eof) { randy.seek(0); } catch (IOException ioe) {} ... try { randy.close(); } catch (IOException ioe) {}

Reading Files in an Applet

In most cases it is possible to read a file and use its contents in a Java applet as long as the file comes from the same directory as the applet (or a sub-directory).

In this case, easier to use BufferedInputStream (instead of FileInputStream) because BufferedInputStream has constructor that accepts InputStream object which is what URL's openStream() method returns

Must catch IOException in case URL's openStream() method fails

Applet below retrieves and displays files from current document's home directory. Enter filename relative to current document's home directory in textfield below. You can use illinois.txt, indiana.txt, michigan.txt, and ohio.txt to get some exciting information.

import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.net.*; import java.io.*; public class ShowFile extends Applet implements ActionListener { private TextArea fileArea; private TextField fileName; public void init() { Button showIt = new Button("Show It"); Panel topPanel = new Panel(); topPanel.setLayout( new BorderLayout()); topPanel.add("North", new Label( "Enter a URL relative to " + "the current document's " + "home directory", Label.CENTER)); Panel inputPanel = new Panel(); inputPanel.add( new Label("File:")); fileName = new TextField(20); inputPanel.add(fileName); showIt.addActionListener (this); inputPanel.add(showIt); topPanel.add("Center",inputPanel); setLayout(new BorderLayout()); add("North", topPanel); fileArea = new TextArea(); fileArea.setFont(new Font ("Courier", Font.BOLD, 12)); add("Center", fileArea); } public void actionPerformed (ActionEvent e) { try { URL file = new URL (getDocumentBase(), fileName.getText()); BufferedInputStream buffer = new BufferedInputStream (file.openStream()); DataInputStream in = new DataInputStream(buffer); fileArea.setText(""); String line; while ((line = in.readLine()) != null) { fileArea.append(line); fileArea.append("\n"); } in.close(); } catch(IOException ioe) { fileArea.setText ("Bad file name!"); } } }

Some Class File methods

java.io package contains Class File

File name = new File (String name);

name is absolute path or relative path to file or folder

File accounts = new File ("accounts.txt");

File accounts = new File ("data/accounts.txt");

File name = new File (String path, String name);

File accounts = new File ("data", "accounts.txt");

boolean canRead() = true if file readable, else false

if (accounts.canRead()) {...}

boolean canWrite() = true if file writeable, else false

boolean exists() = true if file or directory already exists

boolean isFile() = true if name is file, false if directory

boolean isDirectory()

long length()
accounts.length() is length of file in bytes

Data Structures

Static data structures = fixed length

Dynamic data structures = grow and shrink as needed during execution

We will implement data structures using classes, inheritance, composition

Self-Referencing Classes

Self-referencing class = one or more data members are object of that class

class ListNode { Object data; ListNode next; ... }

ListNode next usually referred to as "link" or "pointer" ... can be "next" in sense of sorted order, chronological order, priority, etc.

Self-referencing objects can be used for List, Stack, Queue, Tree

If link is null, no object follows

Dynamic Memory Allocation

Dynamic memory allocation necessary for dynamic data structures

Obtain needed memory, release unneeded memory during execution

Java operator "new" does dynamic memory allocation

ListNode work = new ListNode (data, next);

Linked List

List is Abstract Data Type used in many CS applications

List is collection of nodes each with link to next one

List has beginning link to first node

List has last node with "null" link

Insertions and deletions can be made anywhere in List

List is good choice when no idea about size of data structure or if may vary wildly during execution

class Vector is Java's version of dynamic Array

Tradeoff: List = dynamic; Array = no overhead for dynamically allocating memory, updating links

Operations:

insertAtFront -- put node at front of List

insertAtBack -- put node at end of List

removeFromFront -- remove node from front of List

removeFromBack -- remove node from end of List

isEmpty -- ask if List is empty

Alternative operations:

print -- list contents of List in order

insertAtMiddle -- put node somewhere in List

removeFromMiddle -- remove node from somewhere in List

reportFront -- read node from front of List

class ListNode { Object data; ListNode next; ListNode( Object o, ListNode nextNode ) { data = o; next = nextNode; } ListNode( Object o ) { this( o, null ); } Object getObject() { return data; } ListNode getnext() { return next; } } public class List { ListNode firstNode; ListNode lastNode; String name; public List( String s ) { name = s; firstNode = lastNode = null; } public List() { this( "list" ); } public void insertAtFront( Object insertItem ) { if ( isEmpty() ) firstNode = lastNode = new ListNode( insertItem ); else firstNode = new ListNode( insertItem, firstNode ); } public void insertAtBack( Object insertItem ) { if ( isEmpty() ) firstNode = lastNode = new ListNode( insertItem ); else lastNode = lastNode.next = new ListNode( insertItem ); } public Object removeFromFront() throws EmptyListException { Object removeItem = null; if ( isEmpty() ) throw new EmptyListException( name ); removeItem = firstNode.data; if ( firstNode.equals( lastNode )) firstNode = lastNode = null; else firstNode = firstNode.next; return removeItem; } public Object removeFromBack() throws EmptyListException { Object removeItem = null; if ( isEmpty() ) throw new EmptyListException( name ); removeItem = lastNode.data; if ( firstNode.equals( lastNode )) firstNode = lastNode = null; else { ListNode current = firstNode; while (current.next !=lastNode) current = current.next; lastNode = current; current.next = null; } return removeItem; } public boolean isEmpty() { return firstNode == null; } public void print() { if ( isEmpty() ) { System.out.println( "Empty " + name ); return; } System.out.print( "The " + name + " is: " ); ListNode current = firstNode; while ( current != null ) { System.out.print( current.data.toString() + " "); current = current.next; } System.out.println( "\n" ); } } public class EmptyListException extends RuntimeException { public EmptyListException( String name ) { super("The " + name + " is empty"); } } public class ListTest { public static void main() { List objList = new List(); Boolean b = new Boolean( true ); Character c = new Character('$'); Integer i = new Integer( 34567 ); String s = new String( "hello" ); objList.insertAtFront( b ); objList.print(); objList.insertAtFront( c ); objList.print(); objList.insertAtBack( i ); objList.print(); objList.insertAtBack( s ); objList.print(); Object removedObj; try { removedObj = objList.removeFromFront(); System.out.println( removedObj.toString() + " removed" ); objList.print(); removedObj = objList.removeFromFront(); System.out.println( removedObj.toString() + " removed" ); objList.print(); removedObj = objList.removeFromBack(); System.out.println( removedObj.toString() + " removed" ); objList.print(); removedObj = objList.removeFromBack(); System.out.println( removedObj.toString() + " removed" ); objList.print(); } catch ( EmptyListException e ) { System.err.println( "\n" + e.toString() ); } } }

Object class allows any kind of node in List

Each node should be object of class that has a toString() method

EmptyListException good way to handle that situation

Stack

Stack is Abstract Data Type used in many CS applications

Stack is like cafeteria trays

Last-In, First-Out (LIFO)

Stack = insertions and deletions can be made only at top of Stack

Operations:

push -- put node on top of Stack

pop -- remove node from top of Stack

isEmpty -- ask if Stack is empty

Alternative operations:

print -- list contents of Stack in order

top -- read node on top of Stack

Stack is constrained form of List

Implement Stack through List inheritance

public class Stack extends List { public Stack() { super( "stack" );} public void push( Object o ) { insertAtFront( o ); } public Object pop() throws EmptyListException { return removeFromFront(); } public boolean isEmpty() { return super.isEmpty(); } public void print(){super.print();} } public class StackTest { public static void main() { Stack objStack = new Stack(); Boolean b = new Boolean( true ); Character c = new Character('$'); Integer i = new Integer( 34567 ); String s = new String( "hello" ); objStack.push( b ); objStack.print(); objStack.push( c ); objStack.print(); objStack.push( i ); objStack.print(); objStack.push( s ); objStack.print(); Object removedObj = null; try { while ( true ) { removedObj = objStack.pop(); System.out.println( removedObj.toString() + " popped" ); objStack.print(); } } catch ( EmptyListException e ) { System.err.println( "\n" + e.toString() ); } } }

List contains other methods (insertAtBack, removeFromBack) that could be used...

objStack.insertAtBack (a);

Sometimes disadvantage of implementing through inheritance

Implement Stack through List composition

public class Stack { List s; public Stack() { s = new List( "stack" ); } public void push( Object o ) { s.insertAtFront( o ); } public Object pop() throws EmptyListException { return s.removeFromFront(); } public boolean isEmpty() { return s.isEmpty(); } public void print() { s.print(); } }

Composition hides methods of List that we do not want

objStack.insertAtBack (a);

...now is compile-time error

Queue

Queue is Abstract Data Type used in many CS applications

Queue is like ticket line

Queue = insertions made at back (tail) and deletions made at front (head) of queue

First-In, First-Out (FIFO)

Operations:

enqueue -- put node at rear of queue

dequeue -- remove node from front of queue

isEmpty -- ask if queue is empty

Alternative operations:

print -- list contents of Queue in order

front -- read node from front of queue

public class Queue { List q; public Queue() { q = new List( "queue" ); } public void enqueue( Object o ) { q.insertAtBack( o ); } public Object dequeue() throws EmptyListException { return q.removeFromFront(); } public boolean isEmpty() { return q.isEmpty(); } public void print() { q.print(); } }

Tree

Tree = data structure with links to one or more sub-trees

Binary tree = tree with links to at most two sub-trees

Refer to sub-trees as left child and right child

Node with no children is leaf node

Binary tree can make searching and sorting more efficient

Binary search tree = nodes in left sub-tree "less than" this node and nodes in right sub-tree "greater than" this node

class TreeNode { TreeNode left; int data; TreeNode right; public TreeNode( int d ) { data = d; left = right = null; } public void insert( int d ) { if ( d < data ) { if ( left == null ) left = new TreeNode( d ); else left.insert( d ); } else if ( d > data ) { if ( right == null ) right = new TreeNode( d ); else right.insert( d ); } } } public class Tree { TreeNode root; public Tree() { root = null; } public void insertNode( int d ) { if ( root == null ) root = new TreeNode( d ); else root.insert( d ); } public void inorderTraversal() { inorderHelper( root ); } private void inorderHelper( TreeNode node ) { if ( node == null ) return; inorderHelper( node.left ); System.out.print(node.data + " "); inorderHelper( node.right ); } }

Binary search tree facilitates duplicate elimination

Tightly packed binary tree = each level has about twice as many nodes as previous level

More Complex Data Structures

Doubly-linked lists = usually next and previous links

Multiply-linked lists = each node is on several lists simultaneously

(This material last modified