Home Accessibility Courses Diary The Mouth Forum Resources Site Map About Us Contact
 
For 2023 (and 2024 ...) - we are now fully retired from IT training.
We have made many, many friends over 25 years of teaching about Python, Tcl, Perl, PHP, Lua, Java, C and C++ - and MySQL, Linux and Solaris/SunOS too. Our training notes are now very much out of date, but due to upward compatability most of our examples remain operational and even relevant ad you are welcome to make us if them "as seen" and at your own risk.

Lisa and I (Graham) now live in what was our training centre in Melksham - happy to meet with former delegates here - but do check ahead before coming round. We are far from inactive - rather, enjoying the times that we are retired but still healthy enough in mind and body to be active!

I am also active in many other area and still look after a lot of web sites - you can find an index ((here))
Fundamental classes java.lang, java.util and others

The publicity says "Java is a simple language". Yes, it is; there are just eight primitive data types:

 byte
 boolean
 char
 double
 float
 int
 long
 short

and objects. So how come you can do so much with Java? Through classes of objects and the methods associated with them - both classes provided as a standard part of the Java Runtime Environment (JRE), and classes provided as extras with the Enterprise Edition or from other bundles / third parties.

JAVA.LANG

Java.lang contains the classes that are most central to the language itself; it's a broad rather than a deep hierarchy, which means that there's a lot of classes in the package, but they tend to be independent of one another.

Firstly, java.lang contains the data wrapper classes - immutable class wrappers around the primitive types which allow you to treat primitives as if they're objects (great for use in collections, which are covered later in this module).

Strings and StringBuffers are a part of java.lang, as are the Math (and from Java 1.3 StrictMath) classes. All of these classes (except StringBuffer) implement the Comparable interface, which provides sorting and searching capabilities.

System, Runtime and Process classes provide low level methods and an API for performing low level system functions and running external processes.

Java.lang also includes the Thread class, and Throwables and exception handling; these are major topics (along with Strings and StringBuffers) and are covered in detail in other modules.

JAVA.UTIL

One of the fundamental packages that you'll use in all but the very simplest of Java applications is the Java utility package java.util. Java.util has been with us since the beginning of Java; it was one of the 8 original packages in Java release 1.0. There have been a number of additions for Java2, and some of those additions have superseded older objects for certain applications.

The utilities in java.util provide the more complex programming structures that you'll want to use in any professionally written classes, but aren't in the basic language. The majority of utility classes are concerned with collections ( objects which can be used to hold groups of objects in different ways to an array, which is the only built in structure for a group of objects), but the package also includes classes for date and time work, resource bundle handling, parsing a string (StringTokenizer) and the Timer API that was introduced at Java 1.3

Java.util also includes sub-packages java.util.jar and java.util.zip

OTHER FUNDAMENTAL PACKAGES

The following packages might also be considered to be fundamental in certain applications:

java.io classes and interfaces for input and output java.math packages for arbitrary precision arithmetic
   (do NOT confuse with java.lang.Math ;-) )
java.net Network access and programming java.text Formatting of numbers, dates, times, etc. java.security A number of packages relating to security

DATA WRAPPERS

What's the difference between a primitive and an object? A primitive is a variable into which you can save an information of one of eight pre-defined types directly, whereas an object is held in a variable which is a reference to (a pointer or a memory address under the hood) of a piece or a number of pieces of information.

Primitives are accessed directly through basic facilities provided in Java, whereas objects are accessed through the object interface. Here's a teaching example that copies and compares primitives and objects, showing you how their behaviour differs:

public class Obj_v_prim {

// Object v Primitive - a comparision example

public static void main (String [] args) {

 float sample = 16.5f;
 Float Sam2 = new Float(16.5f);

 System.out.println("Primitive is "+sample);
 System.out.println("Object is "+Sam2);

// copy and compare - appreciate the difference!

 float sam_copy = sample;
 Float Sam2_copy = Sam2;

 float another = 16.5f;
 Float Another2 = new Float(16.5f);

 System.out.println("\nDoing == comparisons");

 if (sample == sam_copy) {
  System.out.println("sample and sam_copy are equal");
 } else {
  System.out.println("sample and sam_copy differ");
 }

 if (sample == another) {
  System.out.println("sample and another are equal");
 } else {
  System.out.println("sample and another differ");
 }

 if (Sam2 == Sam2_copy) {
  System.out.println("Sam2 and Sam2_copy are equal");
 } else {
  System.out.println("Sam2 and Sam2_copy differ");
 }

 if (Sam2 == Another2) {
  System.out.println("Sam2 and Another2 are equal");
 } else {
  System.out.println("Sam2 and Another2 differ");
 }

 System.out.println("\nDoing \".equals\" comparisons");

 if (Sam2.equals(Sam2_copy)) {
  System.out.println("Sam2 and Sam2_copy are equal");
 } else {
  System.out.println("Sam2 and Sam2_copy differ");
 }

 if (Sam2.equals(Another2)) {
  System.out.println("Sam2 and Another2 are equal");
 } else {
  System.out.println("Sam2 and Another2 differ");
 }


 }

}

This test code starts off by defining a primitive float and an object of type Float (note the significance of the capitalisation here!):

 float sample = 16.5f;
 Float Sam2 = new Float(16.5f);

The text program the prints out the values of the variables:
 
 System.out.println("Primitive is "+sample);
 System.out.println("Object is "+Sam2);

The results they come up with are the same, but you should be aware that's because the "toString" method on class Float turns the number that the object contains into a string, and now because the variable themselves are the same.

The test code then copies each of the variables.

 float sam_copy = sample;
 Float Sam2_copy = Sam2;

What's the result?

 [Diag 2]

Notice that we now have the number 16.5 stored 3 times in memory - twice in the two primitives, but only once in an object as Sam2_copy and Sam2 are both references to the same object.

We then set up two more variables containing 16.5 as well - another is a primitive like sample, and Another2 is an object like Sam2. The capitalisation of the variable names here is just a good convention, whereas it's vital in the type declaration (float v Float).

 float another = 16.5f;
 Float Another2 = new Float(16.5f);

Our test program then goes on to use "==" to compare the various variables that we have, and it reports:

Doing == comparisons
sample and sam_copy are equal
sample and another are equal
Sam2 and Sam2_copy are equal
Sam2 and Another2 differ

With the primitives, all the variables themselves contain the value 16.5 so they're all reported as being equal. With the objects, Sam2 and Sam2_copy contain references to the same Float object, so report as being equal. Another2 contains a reference to a different Float object, and so is reported to differ ... it doesn't matter to the == operator that both of the float objects happen to contain the same number; it only cares that they are different objects.

If you need to compare the contents of "data wrapper" type objects (Boolean, Character, Byte, Short, Integer, Long, Float or Double), use the "equals" method on the object. Although this method compares references for most objects (that's how it's defined in the Object base class), it's overridden for the data wrappers to compare the contents of the object. Thus:

 System.out.println("\nDoing \".equals\" comparisons");

 if (Sam2.equals(Sam2_copy)) {
  System.out.println("Sam2 and Sam2_copy are equal");
 } else {
  System.out.println("Sam2 and Sam2_copy differ");
 }

 if (Sam2.equals(Another2)) {
  System.out.println("Sam2 and Another2 are equal");
 } else {
  System.out.println("Sam2 and Another2 differ");
 }

Yields the results:

Doing ".equals" comparisons
Sam2 and Sam2_copy are equal
Sam2 and Another2 are equal

WHY USE DATA WRAPPERS?

Remember how an array can only hold primitives of one type, but it can hold a whole mixture of objects? Data wrappers give you the ability to hold a whole mixture of information in arrays, and also in other collections as we'll see later in this module. There are also times that it's to your advantage to be able to handle numbers and characters through the more sophisticated object interface rather than as simple primitives.

Aside - Microsoft's C# has dispensed with primitives and treats everything as an object. It's an interesting discussion as to whether this is a good idea, or a step too far!

OTHER METHODS ON FLOAT OBJECTS

You can construct a Float object from a String or from a float or double primitive ... for example:

  Float V2 = null;
  String err = null;
  try {
   V2 = new Float(getParameter("temperature"));
  } catch (NumberFormatException e) {
   err = "Not a valid numeric format";
  } catch (ServletException e) {
   err = "Temperature field not provided";
  }

and you can copy the value of a Float object into a primitive for further onward use in your program:

  float tval = V2.floatValue();
  if (tval < 20.0f) { .....

As from Java 1.2, a number of other methods were added, including a static parseFloat so the code from the example above could have read:

  float tval = Float.parseFloat(getParameter("temperature"));

(You would still need the code to catch the exceptions, of course!).

There's other methods available too - have a quick glance at the reference material for further details.

OTHER METHODS ON OTHER DATA WRAPPER OBJECTS

There are similar methods on other data wrapper objects, but they don't always follow the same exact pattern, and some of their histories vary. Check the manual!

Example - I can write:
  int tval = Integer.parseInt(getParameter("temperature"));
and you'll spot straight away ...
 - the class in named Integer (not Int as you would expect)
 - but the method is called parseInt (and not parseInteger)
You won't spot that "parseInt" was available as long ago as Java release 1.0, even though parseFloat only came in at Java 1.2

JAVA.LANG.MATH

The Math class defines a number of constants and static methods for trigonometry, exponentiation and other mathematical operations. Most of them work on double parameters and return double types. Do note that these methods use the underlying C libraries of the computer that's hosting the Java Runtime Environment and are not implemented in Java, so they're fast but results may not exactly match between platforms.

public class Mathop {

public static void main(String [] args) {

 for (int j=0;j<args.length;j++) {

  try {
  double uservalue = Double.parseDouble(args[j]);
  System.out.println("Input value "+uservalue);

  double uvr = Math.toRadians(uservalue);
  System.out.println("That's "+uvr+" radians");

  double cosine = Math.cos(uvr);
  double primary = Math.acos(cosine);
  System.out.println("Cosine is "+cosine+
   " which has a primary angle of "+uvr);



  } catch (NumberFormatException e) {
   System.out.println("Not a number ["+args[j]+"]");

  } finally {
   System.out.println();
  }
 }
}

}

And in operation:

$ java Mathop -45 0 zero 60 90 200
Input value -45.0
That's -0.7853981633974483 radians
Cosine is 0.7071067811865476 which has a primary angle of -0.7853981633974483

Input value 0.0
That's 0.0 radians
Cosine is 1.0 which has a primary angle of 0.0

Not a number [zero]

Input value 60.0
That's 1.0471975511965976 radians
Cosine is 0.5000000000000001 which has a primary angle of 1.0471975511965976

Input value 90.0
That's 1.5707963267948966 radians
Cosine is 6.123031769111886E-17 which has a primary angle of 1.5707963267948966

Input value 200.0
That's 3.490658503988659 radians
Cosine is -0.9396926207859084 which has a primary angle of 3.490658503988659

$

Other methods available include:
ceil
next whole number above
floor
next whole number below
round
nearest whole number (returns an int or long)
abs
number without sign (make negatives positive!)
pow
raise ro power
max
maximum of two numbers
min
minimum of 2 numbers
exp
exponential
log
natural logarithm
sqrt
square root
tan
tangent
and other trignometic functions.


Do note that method for secant and cosecant, etc, are not provided in this fundmental class.

THE SYSTEM CLASS - MISCELLANUOUS FEATURES

The System class provides a low level interface to system facilities and properties - the examples earlier in this module have already used System.out, and you also have:
 System.err an output print stream for error messages
 System.in an Input stream that you'll use for keyboard input

The getProperty method lets you get information about the Java virtual machine that you're running on, and the underlying operating system.

public class About {

public static void main (String [] args) {

 String [] what = {
  "java.home",
  "java.class.path",
  "java.version",
  "java.vendor",
  "java.vm.version", // Java 1.2, many more like this
  "os.name",
  "os.arch",
  "os.version",
  "file.separator",
  "path.separator",
  "line.separator",
  "user.name",
  "user.home",
  "user.dir" };

 for (int j=0; j<what.length; j++) {
  String current = what[j];
  StringBuffer line = new StringBuffer(current);
  while (line.length() < 20) line.append(" ");
  String result =
   line.append(System.getProperty(current)).toString();
  System.out.println(
   (result.replace('\n','\u00ac').replace('\r','\u00ad')));
  }

 }

}

On the system on which this modeule was written, here are the results:

$ java About
java.home /home/javatools/jdk1.3/jre
java.class.path .:/home/javatools/jdk1.3/lib/tools.jar:/home/javatools/j2sdkee1.3/lib/j2ee.jar:/home/graham/mysqlx/mm.mysql-2.0.8:/home/javatools/jakarta-regexp-1.2/jakarta-regesxp-1.2.jar
java.version 1.3.0
java.vendor Sun Microsystems Inc.
java.vm.version 1.3.0
os.name Linux
os.arch i386
os.version 2.2.16-22
file.separator /
path.separator :
line.separator ¨
user.name graham
user.home /home/graham
user.dir /home/graham/java2
$

Also in System, there's an exit method (which takes a single integer parameter - a return status) to exit from a Java Virtual Machine.

THE SYSTEM CLASS - GARBAGE COLLECTION

Within the Java Virtual machine, memory is allocated dynamically - in other words, as objects are created the memory that is required to hold them is set aside, and when they are destroyed the memory is released.

That's not the whole story. If Java went around cleaning up every time that a piece of memory was released, it would be grossly inefficient - it would be rather like calling up the garbage collector to come round whenever you finished a can of coke, instead of waiting for his visit once or twice a week to collect garbage in bulk.

Java's "Garbage Collector" is run from time to time, usually at a time decided by the Virtual Machine itself; not only does it collate all the memory that's free, but it also runs the finalize method on any objects which have been released.

Here's a very simple class which we can store - err - things in:

public class Thing {

public static int number_of_things = 0;
public String what;

public Thing (String what) {
 this.what = what;
 number_of_things++;
 }

protected void finalize () {
 number_of_things--;
 }

}

Now - let's create a number of things, throw some of them away, and keep counting how many things we have:

public class Handbag {

public static void main (String [] args) {

 Thing [] Ihave = new Thing[args.length];

 for (int j=0; j<args.length; j++) {
  Ihave[j] = new Thing(args[j]);
  }

 System.out.println ("I have "+Thing.number_of_things+
   " things in my handbag");
 Ihave[0] = null;
 System.out.println ("I now have "+Thing.number_of_things+
   " things in my handbag");
 System.out.println ("And now there are "+Thing.number_of_things+
   " things in my handbag");
 Ihave[1] = null;
 System.out.println ("now there are "+Thing.number_of_things+
   " things in my handbag");

 System.out.println ("and at the end there are "+Thing.number_of_things+
   " things in my handbag");

}

}

We would hope that if we start off with 4 things in the handbag, that will decrease to 3 and then 2 things as we drop the first and second things. Alas:

$ java Handbag "Credit Cards" keys money pen
I have 4 things in my handbag
I now have 4 things in my handbag
And now there are 4 things in my handbag
now there are 4 things in my handbag
and at the end there are 4 things in my handbag
$

We can force finalizations methods to be run using the runFinalization method, and we can force garbage collection (with an automatic runFinalization first) using the gc method of the System class. Thus:

public class Mypocket {

public static void main (String [] args) {

 Thing [] Ihave = new Thing[args.length];

 for (int j=0; j<args.length; j++) {
  Ihave[j] = new Thing(args[j]);
  }

 System.out.println ("I have "+Thing.number_of_things+
   " things in my pocket");
 Ihave[0] = null;
 System.out.println ("I now have "+Thing.number_of_things+
   " things in my pocket");
 System.gc();
 System.out.println ("And now there are "+Thing.number_of_things+
   " things in my pocket");
 Ihave[1] = null;
 System.out.println ("now there are "+Thing.number_of_things+
   " things in my pocket");
 System.runFinalization();
 System.gc(); // Odd that I need this - to come back to?
 System.out.println ("and at the end there are "+Thing.number_of_things+
   " things in my pocket");

}

}

Which runs much more as we would expect:

$ java Mypocket coin hankie sweet hole
I have 4 things in my pocket
I now have 4 things in my pocket
And now there are 3 things in my pocket
now there are 3 things in my pocket
and at the end there are 2 things in my pocket
$

A word of caution here - if you find that you have to use the gc and runFinalization methods in your program, you may have a poor design ... really, finalize methods are there to flush buffers to disc, etc, and shouldn't contain code on which a continuing application relies. You may find them useful if you're timing things, or if you have an application in which you want a degree of control over the garbage colelctor.

If you are timing, the getTimeMillis() method returns you the number of milliseconds that have elapsed since 1st January 1970 (as a long), so that you can perform comparisons and get an idea of how things are going; a gc() call before you get the timing will help even out your timing tests, as it helps prevent distortion of statistics caused by an occasional garbage collection.

THE RUNTIME AND PROCESS CLASSES

The Runtime class actually underlies the System and Process classes, and the only method in it that's called directly is often the exec method. The exec method starts a new process running externally to the Java Virtual Machine.

Here's an example that attempts to report on how much disc space you have free ...

import java.io.*;

public class Discfree {

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

 String [] Command = null;

 if (System.getProperty("os.name").equals("Linux")) {
  Command = new String[1];
  Command[0] = "df";
  }
 if (System.getProperty("os.name").equals("Solaris")) {
  Command = new String[2];
  Command[0] = "df";
  Command[1] = "-k";
  }
 if (Command == null) {
  System.out.println("Can't find free space on this OS");
  System.exit(1);
  }

 Process Findspace = Runtime.getRuntime().exec(Command);

 BufferedReader Resultset = new BufferedReader(
   new InputStreamReader (
   Findspace.getInputStream()));

 String line;
 while ((line = Resultset.readLine()) != null) {
  System.out.println(line);
  }
 }
}

If we run that on a Linux system, all is well ...

$ java Discfree
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda3 11087504 8320380 2203904 80% /
/dev/hda1 3244412 1767104 1477308 55% /c
/dev/hda5 3780112 1060100 2720012 29% /d
$

That will work nicely on Solaris too (and other 'nix operating systems should fit in well too ...) but it would need some modifications under Windows.

A WORD OF CAUTION ON SYSTEM CLASSES.

One of the key features of Java is that it's portable - in other words that it will run the same way whatever operating system it's running on, under whatever virtual machine. This feature can be lost to your Java applications if you use the low level classes described in this section of the notes

- Runtime.exec() calls rely on other software that's on the computer
on which you are running you Java Virtual Machine. It's no good calling a Linux program such as df if you're likely have your code on a Windows system.

- System.exit (and many other System methods) aren't available in
applets or Servlets, and many of the system properties have been disabled in applets to avoid security problems such as the owner of a web site being able to find the user's login name.

UTILITY OBJECTS TO HOLD MULTIPLE SIMPLE OBJECTS

If you want to hold a number of objects in a single composite object, we call it a collection. The Java language itself (without any additional classes) supports arrays, which can hold primitives or objects. The java.util package adds a whole further series of classes which can be used to hold multiple objects in various arrangements and with various facilities.

Before we go on to look at these various collection classes, do note one important limitation - they hold multiple OBJECTS; if you want to hold multiple primitives in a collection, you need to use the data type wrappers that we looked at earlier in this module.

The first collection objects we look at - the Vector, Stack and Hashtable (and the enumeration interface) have been available from the beginnings of Java. A complete Collection framework was added at Java 1.2; we'll look at those after we've had a brief look at the older classes.

VECTORS

A Vector is a container for a number of objects that can be reference by their index number (position) within the container - you set up a Vector with a constructor (of course), and you can then add and examine elements using methods such as addElement and elementAt. The size method lets you find out how many elements there are in your vector.

import java.util.*;

/** Using a vector*/

public class holt
   {
   public static void main(String[] args)
      {
      System.out.println("Shortened pack");
      Vector play = new Vector();
      play.addElement(new suspect_card_9(3));
      for (int k=21;k<53;k++)
         play.addElement (new playing_card_9((k==27)?0:k));

// And print out cards!
     for (int k=0;k<play.size();k++)
          {
          card_9 current = (card_9) play.elementAt(k);
          System.out.println(current.getcardname());
       }
   }
}

We need to import the java.util classes:

    import java.util.*;

We declare a new object of class vector:

    Vector play = new Vector();

We add elements to that vector. As we have not specified where in the vector elements are to be added, each will automatically be added on to the end.

      play.addElement(new suspect_card_9(3));
      for (int k=21;k<53;k++)
         play.addElement (new playing_card_9((k==27)?0:k));

We can find out how many elements there are in our vector by using the size method:

    for (int k=0;k<play.size();k++)

And we can get specific elements back by using the elementAt method. Since a vector can hold objects of any class, we must cast our returned element to the correct class.

          card_9 current = (card_9) play.elementAt(k);

There are many more methods available in the vector class.

   * To replace or insert elements:

insertElementAt (obj,posn) (extra element not on end) setElementAt (obj,posn) (replacing an element)

   * To retrieve specific elements:

firstElement() lastElement()

   * To remove objects from a vector:

removeElementAt(posn) (by position) removeElement(obj) (by object; returns flag) removeAllElements()

   * To search for an object by position:

indexOf(obj)

You'll notice that a Vector is like a more flexible version of an array, with an object oriented interface and without the restriction on extention. Elements can be removed in from the middle of a Vector, and the remaining elements simply "move up" to fill the gaps. To allow for all these extra facilities, the internal arrangement of the memory of a Vector uses a "link list", so that it can be dynamically expanded (you didn't have to give a size, did you?) and elements removed and added efficiently anywhere - BUT this means that is you want to step through the data many time, efficiency drops a little.

So you have one further method that lets you copy a vector into an array:

copyInto(array)

STACKS

Java Stack objects are derived from vectors. In other words, they have vector capabilities plus.

In computing/Java terms, a stack is a pile of objects put onto a pile for future (re)use. Like any pile of objects, any new objects are added to the top (push) and it's the top object that is passed back to you when you ask (pop).

Stacks are sometimes referred to as "first in, last out" or FILO stores, since the first object pushed onto a stack remains there until all subsequently pushed objects have been popped.

Methods available for Stack objects include:

push(obj) to stack an object

pop() to remove the top item and return it

also

peek() ro return a copy of the top item but

not remove it

empty() true if and only if the stack is empty

Storing our cards on a stack:

[Image]

import java.util.*;

public class outmarsh
   {
   public static void main(String[] args)
      {

      System.out.println("Shortened pack");
      Stack play = new Stack();
      play.push(new suspect_card_9(3));
      for (int k=21;k<53;k++)
         play.push(new playing_card_9((k==27)?0:k));

// And print out cards!

     while (! play.empty())
          {
          card_9 current = (card_9) play.pop();
          System.out.println(current.getcardname());
       }
   }
}

Will mean they print in reverse order!

HASHES

Arrays have elements which are numbered from zero up, as do Vectors. Stacks are a sub-class of vectors, so they number in the same way.

What if we want to hold a table of information but instead of having the objects in it numbered 0,1,2,3 we want to use some other key sequence?

For example, we might want to have a table of Cluedo suspect cards (in a new class) where the key is the name of the person, and the members are the room in which that person is presently located.

We can use a class called Hashtable to store information of this type. In a Hashtable, each element has two objects associated with it:

   * The key (the moral equivalent of the array subscript 0, 1, 2 etc)
   * The data item (which can be an object of any type, just as in a regular
     array)

Let's actually set up a hash table for three of the suspects:

import java.util.*;

public class hinton {
   public static void main(String[] args)
      {
      Hashtable suspects = new Hashtable();
      suspects.put("White","Kitchen");
      suspects.put("Green","Library");
      suspects.put("Plum","Billiard Room");

Objects of any class can be used as the key and can be held in the hashtable. However, if you are using an object of your own class as the key, you need to provide a method called hashCode [See Java documentation].

The first line of executable code here creates a new instance of an object in class Hashtable, and the three following lines use the put method to add data.

You can put as many objects as you like into a hash table.

If you give a new key, a new entry will be added to the table. If you reuse a key, the old object will be replaced.

The put method added objects to the Hashtable. The get method can be used for getting them back out:

[Image]

      System.out.print ("Who are you looking for? ");
      String who = wellreader.read_word();

      String where = (String) suspects.get(who);

      if (where == null)
         {
            System.out.println ("Don't know where "+who+" is");
         } else {
            System.out.println ("The person "+who+" is in the "+where);
         }

Note the return of null if there is no element matching the key in the hash.

With arrays and the other utility objects, you can use a size method to tell you how many entries you have, and then a for loop (or similar) to step through them.

In the case of Hashtables, though, there is no algorithm which will step through all the keys; you have to use the Enumeration interface.

Let's modify the hinton.java class so that an empty string entered lists out all people and where they are located.

Here's the extra code:

Enumeration names = suspects.keys();
    while (names.hasMoreElements()) {
    String surname = (String) names.nextElement();
    String where = (String) suspects.get(surname);
    System.out.println(surname+" is in the "+where);
    }

If you wish to enumerate through the elements rather than the keys, your can use the elements() method.

Note that the enumerated elements may be returned to you in any order. You cannot say they are "alphabetic" nor "in the order they were put" nor give any other description.

If you are going to use complete objects within your own classes as keys, you must provide a method hashCode and a method equals. See the Java documentation for further details.

We'll come back to look at the Collection classes added in Java 2 shortly, but let's have a look at the Enumerations and StringTokenizer first, as these are of great use in setting up and passing through collections.

ENUMERATIONS

Used by many of the utility classes as a way of returning each element in turn. Three methods to consider:

   * hasMoreElements
   * elements
   * nextElement

Let's rewrite the printing loop of holt.java to use enumeration. This is what we start with:

     for (int k=0;k<play.size();k++)
          {
          card_9 current = (card_9) play.elementAt(k);
          System.out.println(current.getcardname());
       }

And with enumeration it changes to:

     Enumeration loop_through = play.elements();

     while (loop_through.hasMoreElements())
          {
          card_9 current = (card_9) loop_through.nextElement();
          System.out.println(current.getcardname());
       }

The operation does not change.

THE STRINGTOKENIZER

There's a frequent requirement to split down a string into its component elements, or "tokens". For example, to split a sentence into words or a line of data into fields. You could roll your own class and methods to do this, but better to use the StringTokenizer class.

The simplest way to use a StringTokenizer is to construct one to parse a string (passed as a parameter to the contructor), then use enumeration methods to handle the individual tokens. For example:

import java.util.*;
public class Strtok {

public static void main(String [] args) {

 String Demo = "This is a string that we want to tokenize";

 StringTokenizer Tok = new StringTokenizer(Demo);
 int n=0;

 while (Tok.hasMoreElements())
  System.out.println("" + ++n +": "+Tok.nextElement());

 }
}

Which produces

$ java Strtok
1: This
2: is
3: a
4: string
5: that
6: we
7: want
8: to
9: tokenize
$

Alternative constructors allow you to specify a string of characters that cn be used as separators, and also to choose whether you wish to return the separator itself as a token. The nextToken method (similar to nextElement) allows you to specify a new delimiter string if you need to.

The StringTokenizer class is fundamental to much of the String handling that you'll need to do in Java.

The NCSA standard file format for web server access logs is nasty - delimiters vary through each access record, some fields are always quoted and others are never quoted.

Here's a sample line from an access log file - let's extract the URL called, the number of bytes returned to the caller, and the name of the program calling us up (In this example, the visitor is Google's robot; for a human visitor, we would be told which browser and version they are using)

crawl5.googlebot.com - - [02/Sep/2001:01:11:36 -0700] "GET /book/0-7357-0949-1.html HTTP/1.0" 200 6081 "-" "Googlebot/2.1 (+http://www.googlebot.com/bot.html)"

import java.util.*;
import java.io.*; // for test program only

public class Access {

// Access Log file (NCSA format) analysis

String Host;
String Time;
String Request;
String Method;
String URL;
int status;
int size = 0;
String Referer;
String UserAgent;

// crawl5.googlebot.com - - [02/Sep/2001:01:11:36 -0700] "GET /book/0-7357-0949-1.html HTTP/1.0" 200 6081 "-" "Googlebot/2.1 (+http://www.googlebot.com/bot.html)"

public Access (String Data) {

 StringTokenizer Splitter = new StringTokenizer(Data," \t");
 String skip;

 Host = Splitter.nextToken();
 skip = Splitter.nextToken();
 skip = Splitter.nextToken("[");
 Time = Splitter.nextToken(" \t");
 skip = Splitter.nextToken("\"");

 Request = Splitter.nextToken();
 skip = Splitter.nextToken(" \t");

 status = Integer.parseInt(Splitter.nextToken(" \t"));

 try {
  size = Integer.parseInt(Splitter.nextToken(" \t"));
 } catch (Exception e) {
  size = 0;
 }

 skip = Splitter.nextToken("\"");
 Referer = Splitter.nextToken();
 skip = Splitter.nextToken();
 UserAgent = Splitter.nextToken();

 // Use another String Tokenizer on the HTTP Request

 Splitter = new StringTokenizer(Request);
 Method = Splitter.nextToken();
 URL = Splitter.nextToken();

 }
 
// Test code

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 String Line = Source.readLine();

 Access Earliest = new Access(Line);

 System.out.println("Access to web page "+Earliest.URL);
 System.out.println("Information returned (in bytes) "+Earliest.size);
 System.out.println("Called by "+Earliest.UserAgent);


 }
 
}

When we run that code, we get:

$ java Access accesslog.txt
Access to web page /book/0-7357-0949-1.html
Information returned (in bytes) 6081
Called by Googlebot/2.1 (+http://www.googlebot.com/bot.html)
$

COLLECTIONS

As from Java 1.2, a whole range of additional collection objects have been added to java.util which to some extend superceed the Hash, Vector and Stack classes.

ARRAYLISTS

ArrayLists are an alternative to Vectors. Let's see an example of one in use to hold a whole series of Access objects of the sort that we've just defined in our StringTokenizer example:

import java.util.*;
import java.io.*;

public class Arlist {

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 ArrayList Hits = new ArrayList();
 String Line;

 while ((Line = Source.readLine()) != null) {
  Hits.add(new Access(Line));
  }

 int nindex = 0;

 for (int j=0; j<Hits.size(); j++) {
  if ((((Access)Hits.get(j)).URL).equals("/index.html")) nindex++;
  }

 System.out.println("/index.html accessed "+nindex+
  " out of a total of "+Hits.size()+" accesses");

 }
}

The add method adds an object to the end of the ArrayList (with an integer parameter as well, you can insert an element into the middle of a list), and the get method retreives an element. The size method tells you the number of elements in the ArrayList.

HASHSETS

A HashSet is a collection into which you can place a number of Objects, but only one instance of each object is kept. Although a "hashing" technique is used internally, a Hashset is NOT a key and value pair in the same way that the HashTable that we've seen already, nor the HashMap that we'll be seeing shortly.

When might I use a HashSet? If I wanted to get a list of all the unique hosts that have visited my web site (from the accesslog file) then I can simply store the hosts into a HashSet and use an Iterator to report on all the elements - the removal of duplicates has been taken care of for me!

import java.util.*;
import java.io.*;

public class Hset {

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 HashSet Hosts = new HashSet();
 String Line;

 while ((Line = Source.readLine()) != null) {
  Hosts.add((new Access(Line)).Host);
  }

 Iterator It = Hosts.iterator();
 int n=0;
 while (It.hasNext()) {
  System.out.println("" + ++n +": "+It.next());
  }
 }
}

And when we run that, we get:

$ java Hset accesslog.txt
1: ham.ulcc.wwwcache.ja.net
2: ip5.d-fd.com
3: ezspider305.directhit.com
4: ac8a2a48.ipt.aol.com
5: ppp-4-35.5800-7.access.uk.worldonline.com
6: cache-mtc-al06.proxy.aol.com
7: 216-21-202-114.spectrumdsl.net
8: inetgw.il.neustar.com
9: violet.d48.lilly.com
10: sv.us.ircache.net
(etc)
255: nachiket.vsnl.net.in
256: 194.216.208.232
257: wwwcache.lanl.gov
258: wfp2.almaden.ibm.com
259: adsl-64-175-33-48.dsl.pltn13.pacbell.net
260: host217-32-157-185.hg.mdip.bt.net
261: j4017.inktomisearch.com
262: cache1.uwn.unsw.edu.au
$

The sample data file we used here contained 3817 accesses, so each host called up over 10 pages from our web server (sounds impressive, doesn't it, until you realise that each image is a separate hit).

ITERATORS AND GENERAL COLLECTION INTERFACES

There are times that we want to step through all the elemets of a collection - be it a HashSet, an ArrayList (or a LinkedList or a HashMap ...) and we can do so using the iterator interface. There's an example of an Iterator in the previous example. Its mandatory methods are
 hasNext() which returns a boolean
 next() which returns an object

The List interface is implemented on ArrayLists, Vectors, and several other classes; we've seen some of the methods it defines in use already. This interface means that we can use common code to handle any conforming collection object. Methods include
 add(Object Toadd)
 add(int position, Object Toadd)
 addAll(Collection Toadd)
 clear()
 contains(Object Totest)
 get(int position)
 indexOf(Object Tofind)
 isEmpty()
 remove(Object Toscrap)
 remove(int position)
 size()
 toArray()

HASHMAPS

HashMaps are very similar to HashTables, except that they are not syncronized, so faster but may not be threadsafe. It's suggested that you use HashMaps unless you require Java 1.0 or 1.1 compatability, or you're writing a threaded application where several threads may access the Hash at the same time.

Here's a sample program that uses a HashMap to count the number of accesses from each host computer in the Access log file that we've been using as an example. Note once again that we've had to store an Integer Object in the HashMap, as collection objects can't store int (or any other) primitives.

import java.util.*;
import java.io.*;

public class Hmap {

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 HashMap HostCount = new HashMap();
 String Line;

 while ((Line = Source.readLine()) != null) {
  String Host = (new Access(Line)).Host;

  int was = 0;
  Object Got;
  if ((Got = HostCount.get(Host)) != null)
   was = (((Integer)Got).intValue());
  HostCount.put(Host, new Integer(was+1));
 }

 Set HostKeys = HostCount.keySet();
 Iterator It = HostKeys.iterator();
 while (It.hasNext()) {
  String HostNow = (String)(It.next());
  System.out.println(HostNow + " - " + HostCount.get(HostNow));
  }
 }
}

And the results:

$ java Hmap accesslog.txt
ham.ulcc.wwwcache.ja.net - 2
ip5.d-fd.com - 10
ezspider305.directhit.com - 9
ac8a2a48.ipt.aol.com - 1
ppp-4-35.5800-7.access.uk.worldonline.com - 25
cache-mtc-al06.proxy.aol.com - 1
216-21-202-114.spectrumdsl.net - 1
inetgw.il.neustar.com - 16
(etc)
hector.access.one.net - 13
nachiket.vsnl.net.in - 1
194.216.208.232 - 11
wwwcache.lanl.gov - 14
wfp2.almaden.ibm.com - 498
adsl-64-175-33-48.dsl.pltn13.pacbell.net - 1
host217-32-157-185.hg.mdip.bt.net - 2
j4017.inktomisearch.com - 1
cache1.uwn.unsw.edu.au - 1
$

SORTING

If you try to make practical use of the previous examples, you'll discover that you really want your output sorted. If there are two or three record to be listed out, then the order doesn't really matter, but many more and sorting becomes vital.

BASIC SORTING IN JAVA

Prior to Java 1.2, if you wanted to sort you had to "roll your own". Not particularly hard, but sometimes longwinded and you would have had a lot of work to do if you wanted to write an efficient sort routine.

In Java 1.2, Arrays.sort and Collections.sort (those are static methods that can be used on arrays and some utility classes that are collections) will sort your composite objects.

Let's sort the hosts from our HashMap example - we've chosen something slightly awkward to sort, as HashMaps can't be sorted, so we've got a Set of keys out from it. Now ... a Set object can't be sorted either (for a class to be sortable, all the elements need to be in memory - alas, that doesn't happen with a Set), so we have to put all the elements into ... well, we chose a Vector. At last - something we can sort!

import java.util.*;
import java.io.*;

public class Hmapsort {

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 HashMap HostCount = new HashMap();
 String Line;

 while ((Line = Source.readLine()) != null) {
  String Host = (new Access(Line)).Host;

  int was = 0;
  Object Got;
  if ((Got = HostCount.get(Host)) != null)
   was = (((Integer)Got).intValue());
  HostCount.put(Host, new Integer(was+1));
 }

 Set HostKeys = HostCount.keySet();

 Iterator It = HostKeys.iterator();
 Vector HKeys = new Vector();
 while (It.hasNext()) {
  HKeys.add((String)(It.next()));
  }

 Collections.sort(HKeys);

 for (int j=0;j<HKeys.size();j++) {
  String K = (String)(HKeys.elementAt(j));
  Integer V = (Integer)(HostCount.get(K));
  System.out.println(K + " - " + V);
  }

 }
}

The results:

$ java Hmapsort accesslog.txt
130-127-83-233.generic.clemson.edu - 1
132.146.145.128 - 3
141.161.46.81 - 19
144.137.82.201 - 1
146.2wrongs.com - 5
155.136.219.101 - 1
160.129.202.150 - 1
160.83.32.14 - 10
192.75.23.73 - 11
(etc)
webcachewp1b.cache.pol.co.uk - 11
wfp2.almaden.ibm.com - 498
wiseone.z-tel.com - 1
wobbly-bob.leeds.wwwcache.ja.net - 2
wsp01.instinet.com - 1
wwwcache.lanl.gov - 14
xenosaur.excite.com - 1
ying.crosskeys.com - 1
zzz-063255229058.splitrock.net - 1
$

COMPARATOR CLASSES

Let's say you don't like the default sort - our example above sorted alphabetically by the host computer name, but in practice you might want to sort by the top level domain or by the number of hits.

You can define your own sort routine by defining a Comparator class - a class that implements the comparator interface - and passing that comparator class across to sort.

Here's an example that sorts by the number of hits:

import java.util.*;
import java.io.*;

public class Hms2 {

public static HashMap HostCount;

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 HostCount = new HashMap();
 String Line;

 while ((Line = Source.readLine()) != null) {
  String Host = (new Access(Line)).Host;

  int was = 0;
  Object Got;
  if ((Got = HostCount.get(Host)) != null)
   was = (((Integer)Got).intValue());
  HostCount.put(Host, new Integer(was+1));
 }

 Set HostKeys = HostCount.keySet();

 Iterator It = HostKeys.iterator();
 Vector HKeys = new Vector();
 while (It.hasNext()) {
  HKeys.add((String)(It.next()));
  }

 Collections.sort(HKeys, new Bynum());

 for (int j=0;j<HKeys.size();j++) {
  String K = (String)(HKeys.elementAt(j));
  Integer V = (Integer)(HostCount.get(K));
  System.out.println(K + " - " + V);
  }

 }

public static int getCount(String Key) {
  Integer Got = (Integer)(HostCount.get(Key)) ;
  return (Got.intValue());
 }
}

And here's the comparator class:

public class Bynum implements java.util.Comparator {

 public int compare(Object one, Object two) {

 return (Hms2.getCount((String)one) - Hms2.getCount((String)two));

 }

}

When we run that, we get:

$ java Hms2 accesslog.txt
ac8a2a48.ipt.aol.com - 1
cache-mtc-al06.proxy.aol.com - 1
216-21-202-114.spectrumdsl.net - 1
violet.d48.lilly.com - 1
sv.us.ircache.net - 1
(etc)
node1.orchestream.com - 67
crawl5.googlebot.com - 80
crawl3.googlebot.com - 98
63.73.169.11 - 98
crawl4.googlebot.com - 127
crawl1.googlebot.com - 132
cache-haw.cableinet.co.uk - 137
wfp2.almaden.ibm.com - 498
marvin.northernlight.com - 519
cr013r01.sac2.fastsearch.net - 621
$

You'll notice that our comparator class makes a callback to the main class (Hms2) to determine the number of hits for one of the objects held in the HashMap, since all the comparators have been given is the Key. If our sort was based on the key (for example, if we were sorting by top level domain) then this callback would not be necessary.

You'll also notice that the HashMap is now a static class variable in Hms2 - necessary so that it's available to the getCount callback method, and not simply a local variable in main.

THE COMPARABLE INTERFACE

If you don't want to write your own Comparator class, you can also have a class ob objects which implements the comparable interface; String, wrapper classes, and Date classes already implement this interface so that sorting by date (for example) is made easy.

The comparable interface requires that you define two methods:
 compareTo()
 equals()

Classes that implement this interface are said to have a "natural order", and default sorting uses this order. There's nothing to stop you defining another Comparator if you want to sort them differently.

Here's an example using our access log records, sorting them by the number of bytes returned, largest first:

import java.util.*;
import java.io.*; // for test program only

public class Acsort implements Comparable{

// Access Log file (NCSA format) analysis

String Host;
String Time;
String Request;
String Method;
String URL;
int status;
int size = 0;
String Referer;
String UserAgent;

// crawl5.googlebot.com - - [02/Sep/2001:01:11:36 -0700] "GET /book/0-7357-0949-1.html HTTP/1.0" 200 6081 "-" "Googlebot/2.1 (+http://www.googlebot.com/bot.html)"

public Acsort (String Data) {

 StringTokenizer Splitter = new StringTokenizer(Data," \t");
 String skip;

 Host = Splitter.nextToken();
 skip = Splitter.nextToken();
 skip = Splitter.nextToken("[");
 Time = Splitter.nextToken(" \t");
 skip = Splitter.nextToken("\"");

 Request = Splitter.nextToken();
 skip = Splitter.nextToken(" \t");

 status = Integer.parseInt(Splitter.nextToken(" \t"));

 try {
  size = Integer.parseInt(Splitter.nextToken(" \t"));
 } catch (Exception e) {
  size = 0;
 }

 skip = Splitter.nextToken("\"");
 Referer = Splitter.nextToken();
 skip = Splitter.nextToken();
 UserAgent = Splitter.nextToken();

 // Use another String Tokenizer on the HTTP Request

 Splitter = new StringTokenizer(Request);
 Method = Splitter.nextToken();
 URL = Splitter.nextToken();

 }

public int compareTo(Object second) {
 return (((Acsort)(second)).size - size);
 }

public boolean equals(Acsort second) {
 int diff = size - second.size;
 return (diff == 0);
 }

 
// Test code

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

 BufferedReader Source = new BufferedReader(
   new FileReader( args[0]));

 String Line;
 Vector Actable = new Vector();

 while ((Line = Source.readLine()) != null) {
  Actable.add(new Acsort(Line));
  }

 Collections.sort(Actable);

 for (int j=0; j<20; j++) {
  Acsort current = (Acsort)(Actable.get(j));
  System.out.println ("URL "+current.URL+
   " from "+current.Host+
   " was "+current.size+ " bytes");
  }


 }
 
}


And the results:

$ java Acsort accesslog.txt
URL /net/search.php4?search=C%2B%2B from febris.mcc.wwwcache.ja.net was 108734 bytes
URL /resources/questions.html from 203.197.108.181 was 44383 bytes
URL /resources/questions.html from inetgw.il.neustar.com was 44383 bytes
URL /resources/questions.html from 195-23-189-183.nr.ip.pt was 44383 bytes
URL /resources/questions.html from cip114.ccc-cable.net was 44383 bytes
URL /resources/questions.html from cr013r01.sac2.fastsearch.net was 44383 bytes
URL /net/search.php4?search=flights+to+munich from webcachew01c.cache.pol.co.uk was 43613 bytes
URL /wellimg/gje.jpg from cache-haw.cableinet.co.uk was 37527 bytes
URL /wellimg/gje.jpg from smtp.deltaimpact.co.uk was 37527 bytes
URL /wellimg/gje.jpg from 24-168-195-156.ff.cox.rr.com was 37527 bytes
URL /wellimg/gje.jpg from cache-haw.cableinet.co.uk was 37527 bytes
URL /resources/library.html from 194.73.75.22 was 34333 bytes
URL /resources/library.html from node1.orchestream.com was 34333 bytes
URL /resources/library.html from 63.73.169.11 was 34333 bytes
URL /resources/library.html from crawl1.googlebot.com was 34333 bytes
URL /resources/library.html from wfp2.almaden.ibm.com was 34333 bytes
URL /resources/library.html from salt.mcc.wwwcache.ja.net was 34333 bytes
URL /resources/library.html from twocats.demon.co.uk was 34333 bytes
URL /resources/library.html from ppp-4-35.5800-7.access.uk.worldonline.com was 34333 bytes
URL /resources/library.html from cr013r01.sac2.fastsearch.net was 34333 bytes
$

EXERCISE

Write a class of objects of type Person. For each Person, you'll have a name and a job title, a constructor that takes two parameters, and a two accessor methods.

Write a second class. This one should have a main method which:

a) creates a hashtable of objects of type person. The keys will be strings - the name of the host computer that a person is using - and the values held in the hashtable will be the person obects.

b) uses an enumeration to loop through the hashtable, printing out each person's name, job title and computer name.

Here's some sample data:

Graham Ellis Trainer dupiaza
John Jones Team Leader vindaloo
Juanita Hamilton Support Specialist balti
Kathy Codd Systems Analyst samosa


See also Training on Java Utility classes

Please note that articles in this section of our web site were current and correct to the best of our ability when published, but by the nature of our business may go out of date quite quickly. The quoting of a price, contract term or any other information in this area of our website is NOT an offer to supply now on those terms - please check back via our main web site

Related Material

Java - Fundamental classes
  [42] - ()
  [1062] - ()
  [1502] - ()
  [1910] - ()
  [2323] - ()
  [2418] - ()
  [2421] - ()
  [2649] - ()
  [2734] - ()
  [2920] - ()
  [3048] - ()
  [4330] - ()
  [4396] - ()
  [4421] - ()
  [4431] - ()

resource index - Java
Solutions centre home page

You'll find shorter technical items at The Horse's Mouth and delegate's questions answered at the Opentalk forum.

At Well House Consultants, we provide training courses on subjects such as Ruby, Lua, Perl, Python, Linux, C, C++, Tcl/Tk, Tomcat, PHP and MySQL. We're asked (and answer) many questions, and answers to those which are of general interest are published in this area of our site.

You can Add a comment or ranking to this page

© WELL HOUSE CONSULTANTS LTD., 2024: Well House Manor • 48 Spa Road • Melksham, Wiltshire • United Kingdom • SN12 7NY
PH: 01144 1225 708225 • FAX: 01144 1225 793803 • EMAIL: info@wellho.net • WEB: http://www.wellho.net • SKYPE: wellho

PAGE: http://www.wellho.net/solutions/java-fun ... thers.html • PAGE BUILT: Wed Mar 28 07:47:11 2012 • BUILD SYSTEM: wizard