Consider a simple example: (source file )
public class uniform_random {
// Basic Lehmer generator - constants
static final long m = 2147483647L;
static final long a = 48271L;
static final long q = 44488L;
static final long r = 3399L;
// "Global" variable - the seed, set to some
// arbitrary non-zero value.
static long r_seed = 12345678L;
// Basic Lehmer generator - uniform[0,1]
// For more information see Knuth, Vol. II.
public static double uniform ()
{
long hi = r_seed / q;
long lo = r_seed - q * hi;
long t = a * lo - r * hi;
if (t > 0)
r_seed = t;
else
r_seed = t + m;
return ( (double) r_seed / (double) m );
}
public static void main (String[] argv)
{
// Let's test the generator. The mean should be 0.5.
double sum = 0;
for (int i=1; i<=10000; i++)
sum += uniform ();
System.out.println ("Average of 10000 samples: " + sum/10000);
}
}
Note:
Let's modify the code so that the seed can be set via a method: ( source file )
public class uniform_random2 {
// Basic Lehmer generator - constants
static final long m = 2147483647L;
static final long a = 48271L;
static final long q = 44488L;
static final long r = 3399L;
static long r_seed = 12345678L;
// Set the seed to any given value.
public static void set_seed (long value)
{
r_seed = value;
}
// Basic Lehmer generator - uniform[0,1]
// For more information see Knuth, Vol. II.
public static double uniform ()
{
long hi = r_seed / q;
long lo = r_seed - q * hi;
long t = a * lo - r * hi;
if (t > 0)
r_seed = t;
else
r_seed = t + m;
return ( (double) r_seed / (double) m );
}
public static void main (String[] argv)
{
// Let's test the generator again. The mean should be 0.5.
set_seed (13579);
double sum = 0;
for (int i=1; i<=10000; i++)
sum += uniform ();
System.out.println ("Average of 10000 samples: " + sum/10000);
}
}
Note:
In-class exercise 3.1: Modify the above code to add a method that computes the area of a circle of given (parameter) radius. Then use the method to compute the mean area of a circle whose radius is uniformly distributed in the range (0,1). Thus, generate a number of circles randomly (according to the above distribution), compute the area of each and take the average. What is the theoretical answer?
We have already seen an example of passing parameters by value. What about call-by-reference?
Rule: all parameters in Java methods are passed by-value.
Thus, no "swap" method is possible in Java.
What happens if we try to implement "swap"? Let's see: ( source file )
public class swap {
public static void swap (int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
public static void main (String[] argv)
{
int i=5, j=6;
swap (i,j);
System.out.println ("i=" + i + " j=" + j);
// What gets printed out?
}
}
Why didn't they provide call-by-reference in Java?
What do you do when you want a method to modify a variable?
x = func (a,b,c); // x gets modified
Suppose we want to add the following methods to our uniform_random generator:
One option is to define methods as follows:
public class uniform_random3 {
// Basic Lehmer generator - constants
static final long m = 2147483647L;
static final long a = 48271L;
static final long q = 44488L;
static final long r = 3399L;
static long r_seed = 12345678L;
// Basic Lehmer generator - uniform[0,1]
// For more information see Knuth, Vol. II.
public static double uniform ()
{
long hi = r_seed / q;
long lo = r_seed - q * hi;
long t = a * lo - r * hi;
if (t > 0)
r_seed = t;
else
r_seed = t + m;
return ( (double) r_seed / (double) m );
}
// U[a,b] generator
public static double uniform_range (double a, double b)
{
// Actual code in here
}
// Discrete Uniform random generator - returns an
// integer between a and b
public static long discrete_uniform (long a, long b)
{
// Actual code in here
}
public static void main (String[] argv)
{
// Use the methods ...
}
}
Java allows you to overload method names as long as the methods' signatures are unique.
Thus, instead of defining uniform_range and discrete_uniform above, we could simply use uniform: ( source file )
// We need to use the math library.
public class uniform_random4 {
// Basic Lehmer generator - constants
static final long m = 2147483647L;
static final long a = 48271L;
static final long q = 44488L;
static final long r = 3399L;
static long r_seed = 12345678L;
// Basic Lehmer generator - uniform[0,1]
// For more information see Knuth, Vol. II.
public static double uniform ()
{
long hi = r_seed / q;
long lo = r_seed - q * hi;
long t = a * lo - r * hi;
if (t > 0)
r_seed = t;
else
r_seed = t + m;
return ( (double) r_seed / (double) m );
}
// U[a,b] generator
public static double uniform (double a, double b)
{
if (b > a)
return ( a + (b-a) * uniform() );
else {
System.out.println ("ERR in uniform(double,double):a="+a+"b="+b);
return 0;
}
}
// Discrete Uniform random generator - returns an
// integer between a and b
public static long uniform (long a, long b)
{
if (b > a) {
double x = uniform ();
long c = ( a + (long) Math.floor((b-a+1)*x) );
return c;
}
else if (a == b)
return a;
else {
System.out.println ("ERR: in uniform(long,long):a="+a+"b="+b);
return 0;
}
}
public static void main (String[] argv)
{
// Use the functions ...
double x = uniform ();
double y = uniform (2.718, 3.141);
long i = uniform (7, 16);
System.out.println ("x=" + x + " y=" + y + " i=" + i);
}
}
Note:
class uniform_random {
public static double uniform (double a, double b)
{
}
// This has the same signature.
public static long uniform (double a, double b)
{
}
}
public static double uniform (double a, b)
import java.lang.*;
even though not needed here, imports all classes
in java.lang.
(We will see what this means later).
We have seen how to print stuff to the screen using System.out.print() or System.out.println().
For some undiscernible reason, Java does not provide any convenient methods for screen input.
We will now, step by step, create a bunch of methods that read input from the screen, which we can use later.
First, here's how we read a line of input from the screen: ( source file )
import java.io.*;
public class screen_io1 {
public static void main (String[] argv)
{
// Put out a prompt.
System.out.print ("Enter string: ");
// We have to have a try clause because
// the method readLine throws an exception.
try {
// These are the key steps in setting up the read operation.
InputStreamReader isr = new InputStreamReader (System.in);
LineNumberReader lr = new LineNumberReader (isr);
// Now read the input line.
String input_line = lr.readLine ();
// Echo it.
System.out.println ("Echo: " + input_line);
}
catch (IOException e) {
// If there was a problem...
System.out.println (e);
}
}
}
Next, to read an integer (all by itself on a line), we need toparse the input line: ( source file )
import java.io.*;
public class screen_io2 {
public static void main (String[] argv)
{
System.out.print ("Enter an integer: ");
try {
InputStreamReader isr = new InputStreamReader (System.in);
LineNumberReader lr = new LineNumberReader (isr);
String input_line = lr.readLine ();
// Parse for an integer.
int i = 0;
try {
i = Integer.parseInt (input_line);
}
catch (NumberFormatException e) {
System.out.println ("Error in input");
System.exit (0);
}
// Echo it.
System.out.println ("The integer you entered: " + i);
}
catch (IOException e) {
System.out.println (e);
}
}
}
Note:
try {
//... some code
}
catch () {
//... some other code
}
statement. In these cases, we are forced to by the
compiler since the methods we call throw exceptions.
import java.io.*;
Let us put this code into useful methods: ( source file )
import java.io.*;
public class screen_io3 {
// Read in a string after putting out a prompt.
public static String read_string (String prompt)
{
OutputStreamWriter osw = new OutputStreamWriter (System.out);
BufferedWriter bw = new BufferedWriter (osw);
try {
bw.write (prompt);
bw.flush (); // Need to flush buffer to ensure output
}
catch (IOException e) {
System.out.println ("new_io::read_string_prompt1: cannot write\n");
System.exit (1);
return "";
}
InputStreamReader isr = new InputStreamReader (System.in);
LineNumberReader lr = new LineNumberReader (isr);
try {
String s = lr.readLine ();
return s;
}
catch (IOException e) {
System.out.println ("new_io::read_string_prompt1: cannot read\n");
System.exit (1);
return "";
}
}
// Read in an int.
public static int read_int (String prompt)
{
String s = read_string (prompt);
while (true) {
try {
int i = Integer.parseInt (s);
return i;
}
catch (NumberFormatException e) {
s = read_string (prompt);
}
}
}
public static void main (String[] argv)
{
// Example:
int i = read_int ("Enter an int: ");
System.out.println ("What you entered: " + i);
}
}
The Java approach to I/O in general is similar to that of C++:
Since a file is just an input stream, reading from a file is similar to reading from the screen, provided the file is identified as an input stream.
The following example shows how to read a text file line-by-line: ( source file )
import java.io.*;
public class file_io1 {
public static void main (String[] argv)
{
try {
// These are the key steps in setting up the read operation.
FileReader fr = new FileReader ("testdata");
LineNumberReader lr = new LineNumberReader (fr);
// Now read the input lines
boolean over = false;
int i = 1;
do {
// Get a line from the file.
String input_line = lr.readLine ();
if (input_line != null) {
System.out.println ("Line " + i + ": " + input_line);
i++;
}
else
over = true;
} while (! over);
// Done.
lr.close();
}
catch (IOException e) {
// If there was a problem...
System.out.println (e);
}
}
}
Note:
Here's the same example with a (simpler) while-loop: (source file )
public static void main (String[] argv)
{
try {
FileReader fr = new FileReader ("testdata");
LineNumberReader lr = new LineNumberReader (fr);
// Using a while loop ...
String input_line = lr.readLine ();
int i = 0;
while (input_line != null) {
i ++;
System.out.println ("Line " + i + ": " + input_line);
input_line = lr.readLine ();
}
// Done.
lr.close();
}
catch (IOException e) {
// If there was a problem...
System.out.println (e);
}
}
Similar to reading, writing is an output stream activity. By defining a file as an output stream, you can write to a file: ( source file )
import java.io.*;
public class file_io2 {
public static void main (String[] argv)
{
try {
// Need to associate a file with a PrintWriter.
// The last parameter is set to "true" to indicate
// auto-flush should be activated.
FileWriter fr = new FileWriter ("testdata2");
PrintWriter pw = new PrintWriter (fr, true);
// Now we're ready for writing.
pw.println ("Hello");
pw.println ("Hello again");
// Done.
pw.close();
}
catch (IOException e) {
System.out.println (e);
}
}
}
Note:
Reading and writing to the same file can be done in many ways. We will consider the following simple task:
Example: ( source file )
import java.io.*;
public class file_io3 {
public static void main (String[] argv)
{
String filename = "testdata3";
int n_lines = 0;
String[] line_buffer = null;
// First, we read in the file to get the size.
try {
FileReader fr = new FileReader (filename);
LineNumberReader lr = new LineNumberReader (fr);
boolean over = false;
n_lines = 0;
do {
String input_line = lr.readLine ();
if (input_line != null)
n_lines ++;
else
over = true;
} while (! over);
lr.close();
}
catch (IOException e) {
System.out.println (e);
}
// We have now counted the number of lines and
// we will read the file into a buffer.
try {
FileReader fr = new FileReader (filename);
LineNumberReader lr = new LineNumberReader (fr);
// Allocate necessary space.
line_buffer = new String[n_lines];
int i = -1;
boolean over = false;
do {
String s = lr.readLine ();
if (s == null)
over = true;
else
line_buffer [++i] = s;
} while (! over);
lr.close();
}
catch (IOException e) {
System.out.println (e);
}
// Next, write the buffer out to the same file
// with line numbers added.
try {
// Open the file for writing
FileWriter fr = new FileWriter (filename);
PrintWriter pw = new PrintWriter (fr, true);
// Write from buffer with line numbers:
for (int i=0; i < n_lines; i++)
pw.println ("Line " + (i+1) + ": " + line_buffer[i]);
pw.close();
}
catch (IOException e) {
System.out.println (e);
}
}
}
To append to a file, simply use the appropriate arguments to FileWriter: include "true" to indicate appending.
Example: ( source file )
import java.io.*;
import java.io.*;
public class file_io_append {
public static void main (String[] argv)
{
try {
// Using "true" as a second argument indicates "append"
// in FileWriter.
FileWriter fr = new FileWriter ("testdata_append", true);
PrintWriter pw = new PrintWriter (fr, true);
// Now we're ready for writing.
pw.println ("Hello");
pw.println ("Hello again");
// Done.
pw.close();
}
catch (IOException e) {
System.out.println (e);
}
}
}
Consider an input file that looks like this:
Circle: center.x=50 center.y=60 radius=20 Circle: center.x=320 center.y=180 radius=40 Rectangle: topleft.x=50 topleft.y=400 bottomright.x=500 bottomright.y=40
What we would like to do is to parse the input to extract the numbers.
Here is how it can be done ( source file ):
// We need to import the StringTokenizer object in
// the java.util package:
import java.util.*;
public class file_io4 {
// Parameters:
// in_string: the property string (e.g. "radius=0.9")
// property: the property name (e.g., "radius")
// The value is assumed to be a real number.
public static double read_property (String in_string, String property)
{
// Obtain a copy of the StringTokenizer object.
StringTokenizer st = new StringTokenizer (in_string);
// Get the first token, specifying the delimiter.
String first_part = st.nextToken ("=");
// Trim whitespace on either side - a String method.
first_part = first_part.trim ();
System.out.println ("First part: " + first_part);
if (!first_part.equals (property)){
System.out.println ("Improper string: " + in_string);
System.exit (0);
}
// Get the next token, now allowing for end-of-line.
String second_part = st.nextToken (" =\t\n\r");
// Trim whitespace.
second_part = second_part.trim();
System.out.println ("Second part: " + second_part);
// Read the number in the second part using the library's
// Double object.
try {
double d = Double.parseDouble (second_part);
return d;
}
catch (NumberFormatException e) {
System.out.println ("Second part not a number: " + second_part);
System.exit (0);
}
return 0;
}
public static void main (String[] argv)
{
// Test
double d = read_property ("x=5", "x");
System.out.println (d);
d = read_property ("center.x=5", "center.x");
System.out.println (d);
d = read_property ("center.x=5 ", "center.x");
System.out.println (d);
d = read_property (" center.x = 5 ", "center.x");
System.out.println (d);
}
}
Note:
import java.util.*;
In-class exercise 3.2: Use the above method, and the methods for reading to a file for the following task. Suppose we want to extract information from a file containing a description of a circle. For example (source file):
Circle: center.x=50 center.y=60 radius=20You are to prompt for the file name, read the file, extract the information and print out the parameters of the circle, and its area. Hints:
Since the above methods of reading an integer etc are useful, let's a create a program unit or module to contain these methods.
We will rewrite the file as follows in the file read_geodata2.java
import java.io.*;
import java.util.*;
class useful_io {
// Read a string from the screen.
static String read_string (String prompt)
{
// Code not shown
}
// Read an int from the screen.
public static int read_int (String prompt)
{
// Code not shown
}
// Read a double from the screen.
public static double read_double (String prompt)
{
// Code not shown
}
// Parse a string for a property.
public static double read_property (String in_string, String property)
{
// Code not shown
}
} // End of class useful_io
public class read_geodata2 {
public static void main (String[] argv)
{
// Get file name from user
file name = useful_io.read_string ("Enter filename: ");
// ... rest of code not shown.
}
}
Note:
file name = useful_io.read_string ("Enter filename: ");
Why is this form of encapsulation a good thing?
To make a collection of methods truly useful, the encapsulation should be placed in a separate file.
We will place our I/O methods in the file useful_io.java :
import java.io.*;
import java.util.*;
public class useful_io {
// Read a string from the screen.
public static String read_string (String prompt)
{
// Code not shown
}
// Read an int from the screen.
public static int read_int (String prompt)
{
// Code not shown
}
// Read a double from the screen.
public static double read_double (String prompt)
{
// Code not shown
}
// Parse a string for a property.
public static double read_property (String in_string, String property)
{
// Code not shown
}
} // End of class useful_io
Next, these methods will be used in the file read_geodata3.java:
import java.io.*;
public class read_geodata3 {
public static void main (String[] argv)
{
// Get file name
String filename = useful_io.read_string ("Enter filename: ");
// Rest of code not shown
}
} // End of read_geodata3.java
Note:
It compiles!
To help programmers and programming teams manage files, Java provides the ability to package related code.
Consider the following example:
import java.io.*;
import useful_io_pack.*; // Imports the entire package useful_io_pack
public class read_geodata4 {
public static void main (String[] argv)
{
// Get file name
String filename = useful_io_subunit.read_string ("Enter filename: ");
// Rest of code not shown.
}
}
% mkdir useful_io_pack
package useful_io_pack; // Must be the first statement in the file.
import java.io.*;
import java.util.*;
public class useful_io_subunit {
// Read a string from the screen.
static String read_string (String prompt)
{
// Code not shown
}
// Parse a string for a property.
public static double read_property (String in_string, String property)
{
// Code not shown
}
} // End of class useful_io_subunit
What is going on?
setenv CLASSPATH /usr/java/lib:.
Thus, the library is searched first, then the current directory.