cs203 lecture 3 john hurley cal state la. recursion recursion is the technique of breaking down a...

56
CS203 LECTURE 3 John Hurley Cal State LA

Upload: gavin-harrie

Post on 15-Dec-2015

218 views

Category:

Documents


1 download

TRANSCRIPT

CS203 LECTURE 3John Hurley

Cal State LA

Recursion

• Recursion is the technique of breaking down a problem into smaller instances of the same problem.

• In programming, a recursive algorithm is one that directly or indirectly invokes itself• direct recursion: methodA calls methodA• indirect recursion: methodA calls methodB and methodB

calls methodA

Recursion• Recursion is used in many common algorithms. It is also extremely important in functional programming, a long-established paradigm (way of thinking about and practicing programming) which is increasingly important today.

• As you will learn in CS312, recursion is equivalent to iteration. In other words, any problem you can solve with either of these two techniques, you can also solve with the other. However:• a) many problems can be solved with simpler algorithms

using recursion, but • b) most programmers find iteration easier to think about

King of Hearts’ Algorithm4

"Where shall I begin, please your majesty?" he asked."Begin at the beginning," the King said gravely, "and go on till you come to the end; then stop.

Recursive Algorithm

5

doSomething()

1. If you are finished, stop.

2. Otherwise1. Solve part of the problem

2. Run this algorithm

Recursion 6

For example, we can define the operation goHome() this way:

–If you are at home, stop. –Else

• Take one step toward home. • goHome()

Recursion

• Avoid infinite regression (and stack overflows) by defining a condition that indicates that the recursion has extended as far as it can, and that therefore causes termination of the recursion

• Recursion usually involves performing some operation on a sequence of values (for example, a list). The termination condition occurs when there are no values left to process.

Iterative (not Recursive) Factorial

8

function factorial is:input: integer n such that n >= 0output: [n × (n-1) × (n-2) × … × 1] Iterative algorithm

1. create new variable called running_total with a value of 1

2. begin loop1. if n is 0, exit loop 2. set running_total to (running_total × n) 3. decrement n 4. repeat loop

3. return running_total end

Recursive Factorial

9

Calculating the factorial of (a positive integer) n can be reduced to multiplying n times the factorial of n-1.

function factorial:input: integer n such that n >= 0output: [n × (n-1) × (n-2) × … × 1]

1. if n == 0, return 1 2. otherwise, return [ n × factorial(n-1) ]

• Note that this recursive solution has a simpler relationship to the actual definition of factorial than the iterative solution does

Recursive Factorial

10

public class Demo {public static int factorialRecursive(int intIn){

if(intIn ==0) return 1;return intIn * factorialRecursive(intIn -1);

}

public static void main(String[] args) {int num = Integer.parseInt(JOptionPane.showInputDialog(null, "Please "

+ "enter the number whose factorial you would like to compute"));JOptionPane.showMessageDialog(null, "The factorial of " + num + " is " +

factorialRecursive(num));}

}

Stack Overflow• Each method call requires additional space on the call

stack to track the data values for the current instance of the method

• If the stack exceeds the memory available to it, a stack overflow occurs

public class StackOverflowDemo {

int x;

public static void main(String[] args){

int x = 1;

StackOverflowDemo s = new StackOverflowDemo();

s.recurseToOverflow(x);

}

public void recurseToOverflow(int x){

System.out.println("instance " + x);

x = x + 1;

recurseToOverflow(x);

}

}

Stack Overflow• If you get a stack overflow error while doing your work in

this class, you almost certainly have a bug in your code that involves an incorrect termination condition. Think until you figure it out and fix it.

• However, note that we run our programs inside Eclipse, which decides how much space to allocate to the call stack.

Euclid's Algorithm• Find the GCD of two positive integers this way:

Input Two positive integers, a and b.

Output The greatest common divisor, g, of a and b.If a<b, exchange a and b.

Divide a by b and get the remainder, r. If r=0, report b as the GCD of a and b.

Replace a by b and replace b by r. Return to the previous step.

http://www.math.rutgers.edu/~greenfie/gs2004/euclid.html

Euclid's Algorithm// by Robert Sedgewick and Levin Wayne : http://introcs.cs.princeton.edu/java/23recursion/Euclid.java.html

// assumes p > q for simplicity

public class Euclid {

public static int gcd(int p, int q) {// recursive implementation

if (q == 0) return p;

else return gcd(q, p % q);

}

public static int gcd2(int p, int q) {// non-recursive implementation

while (q != 0) {

int temp = q;

q = p % q;

p = temp;

}

return p;

}

public static void main(String[] args) {

int p = Integer.parseInt(JOptionPane.showInputDialog(null, "Please enter the first integer"));

int q = Integer.parseInt(JOptionPane.showInputDialog(null, "Please enter the second integer"));

int d = gcd(p, q);

int d2 = gcd2(p, q);

JOptionPane.showMessageDialog(null, "gcd(" + p + ", " + q + ") = " + d);

JOptionPane.showMessageDialog(null, "gcd(" + p + ", " + q + ") = " + d2);

}

}

Multiple RecursionA recursive problem may have more than one base case and/or more than one recursive call.

The definition of the Fibonacci numbers is recursive, and the nth Fibonacci number can be found using a recursive function.

Fibonacci numbers: f(n) =

Multiple Recursion

public class FibonacciCalculator {

public static void main(String[] args){

FibonacciCalculator f = new FibonacciCalculator();

for(int counter = 0; counter < 10; counter++){

long fib = f.fibonacci(counter);

System.out.println("Fibonacci number No. " + counter + " = " + fib);

}

}

public static long fibonacci(long n) {

// https://www.inf.unibz.it/~calvanese/teaching/04-05-ip/lecture-notes/uni10/node23.html

if (n < 0) return -1; // F(n) is not defined when n is negative

if (n == 0)

return 0;

else if (n == 1)

return 1;

else

return fibonacci(n-1) + fibonacci(n-2);

}

}

Recursion

17

• Recursion can be used to process a list (or array of) values.

Recursion18

package demos;

import java.util.ArrayList;import java.util.List;

//https:www.stackoverflow.com/questions/126756/examples-of-recursive-functionspublic class RecursionExample {

public static void main(String[] args) {String[] sleeplessArray = { "ant", "frog", "goose", "weasel", "child" };List<String> sleeplessList = new ArrayList<String>();for (String s : sleeplessArray)

sleeplessList.add(s);RecursionExample r = new RecursionExample(sleeplessList);

}

public RecursionExample(List<String> animals){System.out.print("There was a ");tellStory(animals);

}

private void tellStory(List<String> sleeplessList) {int last=sleeplessList.size() -1;String animal = sleeplessList.get(last);if(sleeplessList.size() == 1) System.out.println("little " + animal +" who went to sleep");else {

System.out.println("little " + animal +" who couldn't go to sleep, so his mother read him a story about a ");sleeplessList.remove(last);tellStory(sleeplessList);

}System.out.println("so the little " + animal + " went to sleep" );

}

}

Recursion

19

• Recursion can also be used to process a list of values and produce a new list of processed values. • This is at the heart of functional programming, as you will learn in CS332F. In the FP paradigm, neither the old list nor the objects in the list change. We produce a new list containing new values.

• Functional languages like LISP, Haskell, and Scala are designed to make this easy.

• It is less straightforward in OOP, but the new version of Java provides more support for this.

Recursion

20

package demos;

import java.util.ArrayList;import java.util.List;

public class ListEx {

public static void main(String[] args) {List<Integer> origList = new ArrayList<Integer>();for (int counter = 1; counter < 10; counter++) origList.add(counter);ListEx r = new ListEx();List<Integer> newList = r.squareList(origList, null);for(Integer i: newList) System.out.print(i + " ");

}

private List<Integer> squareList(List<Integer> oldList, List<Integer> newList) {int lastIndex = oldList.size() -1;int base = oldList.get(lastIndex);oldList.remove(lastIndex);if(oldList.size() == 0) {

newList = new ArrayList<Integer>();}else newList = squareList(oldList, newList);newList.add(base * base);return newList;

}

}

Recursion

21

• Here is the last example in Haskell:

Function definition:

squarelist :: Num a => [a] -> [a]squarelist [] = []squarelist (x:xs) = (x*x): squarelist xs

Function call:

squarelist[1,2,3,4,5,6,7,8,9]

Backtracking

22

• Some algorithms involve following one path as far as it can go, then backing up to the last point at which a different path could have been chosen and then following that path

• Depth-First Search (DFS) is a classic example. Start at the root of a tree or graph and explore as far as possible along each branch before backtracking.

Depth-First Search

23

Source of picture: https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Depth-first-tree.svg/450px-Depth-first-tree.svg.png

File Listing• Consider the problem of listing all the files in a directory,

including those in all its subdirectories, etc. until every path has been exhausted.

• The natural way to approach this problem is to do it recursively using Depth First Search:• List files in the current directory• Run this algorithm on each subdirectory, continuing as far as we

can go

File Listingpublic class FileLister {

private static StringBuilder indentation = new StringBuilder();

public static void main(String args[]) {

String start = JOptionPane.showInputDialog(null,

"Please enter the starting directory");

getDirectoryContent(start);

}

private static void getDirectoryContent(String filePath) {

File currentDirOrFile = new File(filePath);

if (!currentDirOrFile.exists()) {

return;

} else if (currentDirOrFile.isFile()) {

System.out.println(indentation + currentDirOrFile.getName());

return;

} else {

System.out.println("\n" + indentation + "|_"

+ currentDirOrFile.getName());

indentation.append(" ");

String[] s = currentDirOrFile.list();

if (s != null) {

for (String currentFileOrDirName : currentDirOrFile.list()) {

getDirectoryContent(currentDirOrFile + "\\"

+ currentFileOrDirName);

}

}

if (indentation.length() - 3 > 3) {

indentation.delete(indentation.length() - 3,

indentation.length());

}

}

}

}

Towers Of Hanoi26

• The Towers of Hanoi problem supposes that, at the beginning of time, a group of monks in Hanoi were tasked with moving a set of 64 disks of different sizes between three pegs, according to these rules:• No disk may ever be placed above a smaller disk• The starting position has all the disks, in descending order of size,

stacked on the first peg• The ending position has all the disks in the same order, stacked on the

third peg.

• An optimal solution for n disks requires 2n-1 moves264 = 18,446,744,073,709,551,616 

• There is a nice animation of a 4-disk version of the problem athttps://en.wikipedia.org/wiki/File:Tower_of_Hanoi_4.gif

Towers Of Hanoi

27

• Recursive solution: • For any n > 1, the problem can be solved optimally in this

way:• Solve the problem for n -1 disks, starting at the start post

and ending at the "extra" post.• The remaining disk will be the largest one. Move it to the

finish post. • Then solve the problem for the n-1 disks, moving from

the "extra" post to the finish post• The above recursive procedure is applied recursively until

n = 1

• Before you try to understand the code from the book, try out the puzzle online at http://www.softschools.com/games/logic_games/tower_of_hanoi/

Towers Of Hanoi

28

The key to a Towers Of Hanoi solution is a recursive method like this:

public void move(int disks, int from, int to) {if (disks > 0) {

int other = 3 - from - to;move(disks - 1, from, other);towers[to].add(towers[from].remove());move(disks - 1, other, to);

}}

Tail Recursion

29

• A tail-recursive method is one in which there is one recursive call at the very end of the method, e.g. the examples above of recursive factorial calculations and Euclid's algorithm.

• In many programming languages, compilers can convert tail recursion into iterative code, which is more efficient since it does not create new instances on the call stack and since, in many cases, it can avoid redundant calculations. This is called Tail-Call Optimization, or TCO.

• Java compilers may have this feature in the future, but not at the current time.

Recursive Jokes

30

To understand recursion, read this sentence.

A truly greedy man is one who writes his will and names himself as heir -- from Philogelos, the oldest known book of jokes, compiled about 400 AD.

Function recurse()1. If you are done with your program, stop2. Otherwise,

A. Try to find the problemB. Utter an obscenity

3. recurse()

Binary File I/O

• Real world applications usually use databases to store large amounts of information

• Main exceptions are apps that have unique file formats that can’t easily be translated to databases, like word processors or paint software.

• If you have not studied text file I/O, take a look at the material in the textbook or my lectures from CS202 (same URL as this class’ website, except for the course number.)

• There is a very nice way to store data from OOP applications directly to disk, though: binary file i/o

Binary File I/O• Binary files store data as sequences of bytes in a format

that is usually far more compact than text files. • If you open a binary file in a word processor or text editor,

you will see gibberish. It is possible to see the byte values in base 16 using a hex editor, but they will normally be unintelligible.

• Every application formats its binary files differently. The only way to easily retrieve the data is by using an application that can parse the data, normally the app that generated the file in the first place.

• Programming languages typically offer easy ways to store data in binary files.

• Java and other object-oriented languages can format objects as binary sequences for file storage. They also offer automated ways to recreate objects from the data.

Binary File I/OOutput:• FileOutputStream is a stream for output to a file.• BufferedOutputStream is a stream with a buffer (write data

in chunks for more efficiency, since there are fewer calls to the expensive input/output methods used by the operating system). BufferedOutputStream wraps an unbuffered stream such as a FileOutputStream to provide the buffer.

• DataOutputStream provides methods for writing primitive types as well as Strings to the stream it wraps.

• Methods like writeDouble() output the stated types

All of these have equivalent input methods• The following example stores low-level types like Double

and uses parsing code when the file is read, but you will soon see an example that stores and retrieve collections of objects of programmer-defined classes.

package menudemo;

// http://docs.oracle.com/javase/tutorial/essential/io/examples/DataStreams.java

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.EOFException;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

public class DataStreams {

static final String dataFile = "invoicedata";

static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };

static final int[] units = { 12, 8, 13, 29, 50 };

static final String[] descs = { "Java T-shirt",

"Java Mug",

"Duke Juggling Dolls",

"Java Pin",

"Java Key Chain" };

public static void main(String[] args) throws IOException {

DataOutputStream out = null;

try {

out = new DataOutputStream(new

BufferedOutputStream(new FileOutputStream(dataFile)));

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

out.writeDouble(prices[i]);

out.writeInt(units[i]);

out.writeUTF(descs[i]);

}

} finally {

out.close();

}

DataInputStream in = null;

double total = 0.0;

try {

in = new DataInputStream(new

BufferedInputStream(new FileInputStream(dataFile)));

double price;

int unit;

String desc;

try {

while (true) {

price = in.readDouble();

unit = in.readInt();

desc = in.readUTF();

System.out.format("You ordered %d units of %s at $%.2f%n",

unit, desc, price);

total += unit * price;

}

} catch (EOFException e) {

// this is really how Oracle's documentation suggests you find the end of a file!

System.out.println("EOF reached");}

System.out.format("For a TOTAL of: $%.2f%n", total);

}

finally {

in.close();

}

}

}

Binary I/O• ObjectOutputStream and ObjectInputStream allow binary

I/O of whole objects• Objects read from files must be cast correctly• Objects stored this way must be serializable, meaning

they must implement the Serializable interface, which just identifies them as convertible to a binary stream. Serializable does not require any actual methods.

• If the object contains references to other objects, all the classes must be serializable!

• Key methods are writeObject() and readObject(). readObject() requires a cast, so you have to know what kind of objects are stored in the file you are reading.

Monster Herd Persisterpackage demos;

import java.io.Serializable;

public class Monster implements Serializable {

private String name;

private String hometown;

private String rampageBehavior;

public Monster(String nameIn, String hometownIn, String rampageBehaviorIn) {

name = nameIn;

hometown = hometownIn;

rampageBehavior = rampageBehaviorIn;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getHometown() {

return hometown;

}

public void setHometown(String hometown) {

this.hometown = hometown;

}

public String getRampageBehavior() {

return rampageBehavior;

}

public void setRampageBehavior(String rampageBehavior) {

this.rampageBehavior = rampageBehavior;

}

public String toString() {

return name + " from " + hometown + " likes to " + rampageBehavior;

}

}

package monsterherd;

import java.awt.Dimension;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.KeyEvent;

import java.io.File;

import java.util.List;

import javax.swing.JFileChooser;

import javax.swing.JFrame;

import javax.swing.JMenu;

import javax.swing.JMenuBar;

import javax.swing.JMenuItem;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTable;

public class MonsterGUI {

MonsterHerd herd;

JFrame frame = null;

JPanel panel;

JMenuBar menubar;

JScrollPane scrollPane;

JTable table;

public MonsterGUI() {

herd = new MonsterHerd();

initUI();

}

public final void initUI() {

frame = new JFrame();

panel = new JPanel();

table = setUpTable();

menubar = new JMenuBar();

JMenu file = new JMenu("File");

file.setMnemonic(KeyEvent.VK_F);

final JFileChooser fc = new JFileChooser();

// enums like KeyEvent evaluate to ints

createAndAddMenuItem(file, "Open", KeyEvent.VK_O, "Open File",

new ActionListener() {

public void actionPerformed(ActionEvent event) {

int retVal = fc.showOpenDialog(frame);

if (retVal == JFileChooser.APPROVE_OPTION) {

File selectedFile = fc.getSelectedFile();

herd.populateFromFile(selectedFile);

updateTable();

}

}

});

createAndAddMenuItem(file, "Add Monster", KeyEvent.VK_A, "Add Monster",

new ActionListener() {

public void actionPerformed(ActionEvent event) {

String name = JOptionPane.showInputDialog(frame,

"please enter the monster's name");

String hometown = JOptionPane.showInputDialog(frame,

"please enter the monster's hometown");

String rampage = JOptionPane

.showInputDialog(frame,

"what does the monster do when it goes on a rampage?");

Monster m = new Monster(name, hometown, rampage);

herd.addMonster(m);

updateTable();

}

});

createAndAddMenuItem(file, "Save", KeyEvent.VK_S, "Save File",

new ActionListener() {public void actionPerformed(ActionEvent event) {

int retVal = fc.showOpenDialog(frame);

if (retVal == JFileChooser.APPROVE_OPTION) {

File selectedFile = fc.getSelectedFile();

herd.saveBinary(selectedFile);

}

}

});

createAndAddMenuItem(file, "Exit", KeyEvent.VK_E, "Exit application",

new ActionListener() {

public void actionPerformed(ActionEvent event) {

System.exit(0);

}

});

Monster Herd Persistermenubar.add(file);

frame.setJMenuBar(menubar);

frame.setTitle("Monsters");

frame.setSize(600, 200);

frame.setLocationRelativeTo(null);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

scrollPane = new JScrollPane(table);

panel.add(scrollPane);

frame.add(panel);

frame.setVisible(true);

}

private void createAndAddMenuItem(JMenu menu, String message, int mnemonic,

String tooltip, ActionListener a) {

JMenuItem menuItem = new JMenuItem(message);

menuItem.setMnemonic(mnemonic);

menuItem.setToolTipText(tooltip);

menuItem.addActionListener(a);

menu.add(menuItem);

}

private JTable setUpTable(){

String[] columnNames = { "Name", "Hometown", "Rampage Behavior"};

Object[][] data = listToArray(herd.getMonsters());

JTable newTable = new JTable(data, columnNames);

newTable.setPreferredScrollableViewportSize(new Dimension(500, 70));

newTable.setFillsViewportHeight(true);

newTable.setAutoCreateRowSorter(true);

newTable.doLayout();

return newTable;

}

private void updateTable() {

for (Monster m : herd.getMonsters())

System.out.println(m);

panel.removeAll();

table = setUpTable();

scrollPane = new JScrollPane(table);

panel.add(scrollPane);

panel.doLayout();

}

private Object[][] listToArray(List<Monster> list) {

Object[][] array = new Object[list.size()][3];

for (int counter = 0; counter < list.size(); counter++) {

array[counter][0] = list.get(counter).getName();

array[counter][1] = list.get(counter).getHometown();

array[counter][2] = list.get(counter).getRampageBehavior();

}

return array;

}

public static void main(String[] args) {

MonsterGUI ex = new MonsterGUI();

}

}

Monster Herd Persisterpackage monsterherd;

import java.io.File;

import java.io.Serializable;

import java.util.ArrayList;

import java.util.List;

public class MonsterHerd implements Serializable{

private List<Monster> monsters;

private MonsterHerdPersister persister;

public MonsterHerd(){

monsters = new ArrayList<Monster>();

persister = new MonsterHerdPersister();

}

public List<Monster> getMonsters() {

return monsters;

}

public void saveBinary(File fileOut){

persister.saveHerdToFile(fileOut, this);

}

Monster Herd Persisterpublic void populateFromFile(File fileIn){

monsters = persister.readHerdFromFile(fileIn).getMonsters();

}

public void addMonster(Monster m){monsters.add(m);}

public String toString(){

StringBuilder sb = new StringBuilder();

for(Monster m: monsters) sb.append(m + "\n");

return sb.toString();

}

}

Monster Herd Persisterpackage monsterherd;

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

public class MonsterHerdPersister implements Serializable{

public void saveHerdToFile(File f, MonsterHerd mh) {

ObjectOutputStream out = null;

try {

out = new ObjectOutputStream(new BufferedOutputStream(

new FileOutputStream(f)));

out.writeObject(mh);

out.close();

} catch (Exception e) {

e.printStackTrace();

}

}

Monster Herd Persisterpublic MonsterHerd readHerdFromFile(File f) {

ObjectInputStream in = null;

MonsterHerd m = null;

try {

in = new ObjectInputStream(new BufferedInputStream(

new FileInputStream(f)));

m = (MonsterHerd) in.readObject();

in.close();

} catch (Exception e) {

e.printStackTrace();

}

return m;

}

}

Binary I/O With List• Binary I/O can be used to store and retrieve lists of

objects.

Binary I/O With Listpackage vampirebinaryio;

import java.io.Serializable;

public class Crypt implements Serializable {

private String location;

public Crypt(String location) {

this.location = location;

}

public void setLocation(String location) {

this.location = location;

}

public String getLocation() {

return location;

}

public String toString(){

return "a really mysterious crypt in " + location;

}

}

package vampirebinaryio;

import java.io.Serializable;

import javax.swing.JOptionPane;

public class Vampire implements Serializable {

private String name;

private Crypt crypt;

public Vampire(String name, String location) {

this.name = name;

crypt = new Crypt(location);

}

public void setName(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setLocation(String location) {

crypt.setLocation(location);

}

public String getLocation() {

return crypt.getLocation();

}

public void rampage() {

JOptionPane.showMessageDialog(null, name + " arises from " +crypt.toString() + " and sucks people's blood "

+ "all night, then returns to a coffin to hide from sunlight");

}

public String toString(){

return name + " inhabits " + getLocation();

}

}

Binary I/O With Listpackage vampirebinaryio;

import java.io.File;

import java.util.ArrayList;

import java.util.List;

import javax.swing.JFileChooser;

import javax.swing.JOptionPane;

public class VampireIODriver {

private static List<Vampire> vampires;

public VampireIODriver() {

vampires = new ArrayList<Vampire>();

}

public void prowl() {

String[] options = { "Quit", "List Vampires", "Add A Vampire",

"Save To File", "Load From File" };

int choice = 0;

Binary I/O With Listdo {

choice = JOptionPane.showOptionDialog(null, "Next Action",

"Next Action", JOptionPane.DEFAULT_OPTION,

JOptionPane.QUESTION_MESSAGE, null, options, options[0]);

switch (choice) {

case 1:

listVampires();

break;

case 2:

addVampire();

break;

case 3:

saveToFile();

break;

case 4:

loadFromFile();

break;

}

} while (choice != 0);

}

public void listVampires() {

StringBuilder sb = new StringBuilder();

for (Vampire v : vampires)

sb.append(v.toString() + "\n");

if (sb.length() == 0)

sb.append("no vampires");

JOptionPane.showMessageDialog(null, sb);

}

public void addVampire() {

String name = JOptionPane

.showInputDialog("Please enter the vampire's name");

String location = JOptionPane

.showInputDialog("Please enter the vampire's location");

vampires.add(new Vampire(name, location));

}

public void saveToFile() {

VampirePersister persister = new VampirePersister();

JFileChooser fc = new JFileChooser();

int retVal = fc.showOpenDialog(null);

if (retVal == JFileChooser.APPROVE_OPTION) {

File selectedFile = fc.getSelectedFile();

persister.saveListToFile(selectedFile, vampires);

}

}

Binary I/O With Listpublic void loadFromFile() {

VampirePersister persister = new VampirePersister();

JFileChooser fc = new JFileChooser();

int retVal = fc.showOpenDialog(null);

if (retVal == JFileChooser.APPROVE_OPTION) {

File selectedFile = fc.getSelectedFile();

vampires = persister.readListFromFile(selectedFile);

}

}

public static void main(String[] args) {

VampireIODriver d = new VampireIODriver();

d.prowl();

}

}

Binary I/O With Listpackage vampirebinaryio;

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

import java.util.List;

public class VampirePersister implements Serializable{

public void saveListToFile(File f, List<Vampire> vl) {

ObjectOutputStream out = null;

try {

out = new ObjectOutputStream(new BufferedOutputStream(

new FileOutputStream(f)));

out.writeObject(vl);

out.close();

} catch (Exception e) {

e.printStackTrace();

}

}

Binary I/O With Listpublic List<Vampire> readListFromFile(File f) {

ObjectInputStream in = null;

List<Vampire> vl = null;

try {

in = new ObjectInputStream(new BufferedInputStream(

new FileInputStream(f)));

vl = (List<Vampire>) in.readObject();

in.close();

} catch (Exception e) {

e.printStackTrace();

}

return vl;

}

}