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