CS 4773 Object Oriented Systems
Vectors and Synchronization

Code for the programs can be found in
/usr/local/courses/cs4773/spring2000/examples/set6


Previous Topic: Threads and Synchronization

Vectors
Vector Methods
Stepping Through a Vector
Example: Finding Words in a String
A String Buffer Example
Vectors and Threads
Enumerations
Synchronization Across Objects
Summary of Programs

Next Topic: GirdBagLayout



Vectors

Suppose you want to create a list of items and you do not know how long the list will be when it is first created.

There are several ways of doing this:

To create a vector just do the following:
   Vector v;
   v = new Vector();
You must import java.util.Vector or java.util.*.

A vector can hold any reference type.
The elements of a vector do not have to have the same type.


Vector Methods

Here are some of the important vector methods.
See the vector documentation for details.

Stepping Through a Vector

   Vector v;
   Point P;
   v = new Vector();
   for (int i=0; i < 10; i++)
      v.addElement(new Point(i,i+1));
   for (int i=0; i < v.size(); i++) {
      p = (Point)v.elementAt(i);
      System.out.println("Point has coordinates "+p.x+" and "+p.y);
   }

Example: Finding Words in a String

Problem: Given a String, produce a list of the words.

For our purposes, words are delimited by anything with an ASCII code of a blank or less. Here is some code for doing this.

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

class Vector1 {

   private static String readString(String s) {
      File f;
      FileInputStream fi;
      int len;
      byte[] b;

      f = new File(s);
      len = (int)f.length();
      try {
         fi = new FileInputStream(f);
      }
      catch (FileNotFoundException e) {
         System.out.println("Cannot find file "+s);
         return "";
      }
      System.out.println("File has length "+len);
      b = new byte[len];
      try {
         if (fi.read(b) != len) {
            System.out.println("Error reading from file "+s);
            return "";
         }
      }
      catch (IOException e) {
         System.out.println("Error reading from file "+s);
         return "";
      }
      System.out.println("File reading done");
      return new String(b);
   }

   private static boolean delimTest(char c) {
      if (c < = ' ') return true;
      return false;
   }

   private static int skipDelims(String s, int i) {
      while ( (i < s.length()) && delimTest(s.charAt(i)) )
         i++;
      return i;
   }

   private static void setTokens1(Vector v, String s) {
      String s1;
      char c;
      int i;
      s1 = "";
      i = skipDelims(s,0);
      while (i < s.length()) {
         c = s.charAt(i);
         if ( delimTest(c) ) {
            v.addElement(s1);
            i = skipDelims(s,i);
            s1 = "";
         }
         else {
            s1 = s1 + s.charAt(i);
            i++;
         }
      }
      if (s1.length() > 0)
         v.addElement(s1);
   }

   public static void main(String args[]) {

      String str;
      String infile = "test.in";
      Vector v;
      long time1;
      long time2;

      if (args.length < 1)
         System.out.println("using default file "+infile);
      else
         infile = args[0];
      v = new Vector();
      str = readString(infile);
      time1 = System.currentTimeMillis();
      setTokens1(v,str);
      time2 = System.currentTimeMillis();
      System.out.println("Vector size is "+v.size());
      System.out.println("Time for conversion in milliseconds: "+(time2-time1));
   }
}
Here are the results of running this on a file of moderate size:
     java Vector1
     using default file test.in
     File has length 202171
     File reading done
     Vector size is 23800
     Time for conversion in milliseconds: 736
Why does this take so long?
The string manipulation in setToken1.

Here is a fix that is in Vector2.java

   private static int skipDelims(String s, int i) {
      while ( (i < s.length()) && delimTest(s.charAt(i)) )
         i++;
      return i;
   }

   private static int skipRegular(String s, int i) {
      while ( (i < s.length()) && !delimTest(s.charAt(i)) )
         i++;
      return i;
   }

   private static void setTokens2(Vector v, String s) {
      String s1;
      char c;
      int i;
      int j;
      s1 = "";
      i = skipDelims(s,0);
      while (i < s.length()) {
         j = skipRegular(s,i);
         if (j > i) 
           v.addElement(s.substring(i,j)); 
         i = skipDelims(s,j);
      }
   }
Here are the results of running this on the same input:
     java Vector2
     using default file test.in
     File has length 202171
     File reading done
     Vector size is 23800
     Time for conversion in milliseconds: 129

Here is another way that uses the Java StringTokenizer:

   private static void setTokens3(Vector v, String s) {
      StringTokenizer st;
      st = new StringTokenizer(s);
      while (st.hasMoreTokens())
         v.addElement(st.nextToken());
   }
This is the result of running it:
     java Vector2st
     using default file test.in
     File has length 202171
     File reading done
     Vector size is 23800
     Time for conversion in milliseconds: 198

A String Buffer Example

A similar problem occurs when making long strings out of short ones.

Consider the following code which can be added to the above example and is very inefficient:

      str = "";
      for (int i=0; i < v.size(); i++) {
         str = str + (String)v.elementAt(i) + " ";
      }  
A better solution is:
      StringBuffer sb;

      sb = new StringBuffer();
      for (int i=0; i < v.size(); i++) {
         sb.append((String)v.elementAt(i) + " ");
      }  
      str = sb.toString();
Here are the timing results on the same file as before using a StringBuffer:
     java Vector2sb
     using default file test.in
     File has length 202171
     File reading done
     Vector size is 23800
     Time for making tokens in milliseconds: 128
     String now has length 174738
     Time for conversion to a string in milliseconds: 190
What do you think the time would be for the first method?
java Vector2s
using default file test.in
File has length 202171
File reading done
Vector size is 23800
Time for conversion in milliseconds: 129
String now has length 174738
Time for conversion in milliseconds: 77034
Yes, that is 77,034 milliseconds, or 77 seconds, compared to 190 ms

That is 400 times as long.


Vectors and Threads

The vector methods are synchronized so you will not corrupt a vector by manipulating it with more than one thread.

However, care must still be taken.

Consider the following application:

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

class Vector3 {

   public static void showVector(Vector v) {
      String str;

      for (int i=0;i < v.size();i++) {
         try {
            Thread.sleep(100);
         }
         catch (InterruptedException e) {}
         str = (String)v.elementAt(i);
         System.out.println(i+" :"+str);
      }
   }

   public static void main(String args[]) {

      Vector v;
      BusyThread t;

      v = new Vector();
      t = new BusyThread(v,1000);
      t.start();
      for (int i=0;i < 10;i++) {
        System.out.println("Vector had size "+v.size());
        showVector(v);
        System.out.println("Vector has size "+v.size());
        try {
          Thread.sleep(1000);
        }
        catch (InterruptedException e) {}
      }
   }
}
BusyThread just adds to and removes elements from a vector at random:
import java.util.*;

class BusyThread extends Thread {
   Vector v;
   int count;

   public BusyThread(Vector v, int count) {
      this.v = v;
      this.count = count;
   }

   public void run() {
      double ran;
      for (int i=0;i < count;i++) {
         ran = Math.random();
         if (ran > 0.5)
            v.addElement("abcdef"+i);
         else if (v.size() > 0)
            v.removeElementAt(0);
         try {
            sleep(10);
         }
         catch (InterruptedException e) {}
      }  
   }   
}
The timing is set up to make it likely that the vector will change between when the index is checked against the size of the vector and when the element is gotten from the vector.

Here is some smaple output. The error will occur in different places on different runs, but it will usually appear somewhere:

java Vector3
Vector had size 0
Vector has size 0
Vector had size 14
0 :abcdef40
1 :abcdef45
2 :abcdef49
3 :abcdef57
4 :abcdef63
5 :abcdef70
6 :abcdef75
7 :abcdef82
8 :abcdef88
9 :abcdef94
10 :abcdef102
11 :abcdef109
12 :abcdef113
13 :abcdef119
14 :abcdef126
15 :abcdef135
16 :abcdef149
17 :abcdef155
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 18 >= 18
	at java.lang.Throwable.fillInStackTrace(Native Method)
	at java.lang.Throwable.(Throwable.java:94)
	at java.lang.Exception.(Exception.java:42)
	at java.lang.RuntimeException.(RuntimeException.java:47)
	at java.lang.IndexOutOfBoundsException.(IndexOutOfBoundsException.java:44)
	at java.lang.ArrayIndexOutOfBoundsException.(ArrayIndexOutOfBoundsException.java:53)
	at java.util.Vector.elementAt(Compiled Code)
	at Vector3.showVector(Compiled Code)
	at Vector3.main(Compiled Code)

Enumerations

Another way to step through elements of a vector is with an Enumeration.

An Enumeration is an interface with two methods:

You can create an enumeration from a vector with the elements method: Here is how you might use it in the above example:
class Vector4 {

   public static void showVector(Vector v) {
      String str;
      Enumeration enum;

      enum = v.elements();
      while (enum.hasMoreElements()) {
         try {
            Thread.sleep(100);
         }
         catch (InterruptedException e) {}
         str = (String)enum.nextElement();
         System.out.println(str);
      }  
   }   
...
We can run this and see if it has the same problem.

The vector documentation says:

One way to fix the problem is by catching the exception that is thrown by nextElement
class Vector4c {

   public static void showVector(Vector v) {
      String str;
      Enumeration enum;

      enum = v.elements();
      while (enum.hasMoreElements()) {
         try {
            Thread.sleep(100);
         }
         catch (InterruptedException e) {}
         try {
            str = (String)enum.nextElement();
         }
         catch (NoSuchElementException e) {
            System.out.println("Could not find element");
            return;
         }
         System.out.println(str);
      }  
   }   
...
We can run this and see what happens. Here is some smaple output:
java Vector4c
Vector had size 1
Vector has size 0
Vector had size 4
Could not find element
Vector has size 0
Vector had size 1
abcdef111
abcdef118
Vector has size 2
Vector had size 5
abcdef164
abcdef169
abcdef174
abcdef180
abcdef187
abcdef196
abcdef206
abcdef210
abcdef215
abcdef227
Could not find element
Vector has size 9
Vector had size 22
abcdef253
abcdef258
abcdef265
abcdef268
abcdef272
...

Synchronization Across Objects

Another possible solution is to use a Java monitor to prevent the vector from being modified while it is being accessed.

Recall that each object has its own monitor.
Usually, to protect a piece of code you use the object which contains that code.
Here the code is in two different objects.
We can use the monitor corresponding to the vector itself for the synchronization.


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

class Vector4S {

   public static void showVector(Vector v) {
      String str;
      Enumeration enum;

      synchronized(v) {
         enum = v.elements();
         while (enum.hasMoreElements()) {
            try {
               Thread.sleep(100);
            }
            catch (InterruptedException e) {}
            str = (String)enum.nextElement();
            System.out.println(str);
         }
      }

   }

   public static void main(String args[]) {

      Vector v;
      BusyThreadS t;

      v = new Vector();
      t = new BusyThreadS(v,1000);
      t.start();
      for (int i=0;i < 10;i++) {
        System.out.println("Vector had size "+v.size());
        showVector(v);
        System.out.println("Vector has size "+v.size());
        try {
          Thread.sleep(1000);
        }
        catch (InterruptedException e) {}
      }
   }
}


import java.util.*;

class BusyThreadS extends Thread {
   Vector v;
   int count;

   public BusyThreadS(Vector v, int count) {
      this.v = v;
      this.count = count;
   }

   public void run() {
      double ran;
      for (int i=0;i < count;i++) {
         ran = Math.random();

         synchronized(v) {
            if (ran >  0.5)
               v.addElement("abcdef"+i);
            else if (v.size() > 0)
               v.removeElementAt(0);
         }

         try {
            sleep(10);
         }
         catch (InterruptedException e) {}
      }
   }
}

If you watch Vector4S run carefully, you will see that it does not terminate immediately when it is done printing.
It is waiting for the BusyThreadS thread to exit.
You can make it exit immediately by having the thread run as a daemon:

      t = new BusyThreadS(v,1000);
      t.setDaemon(true);
      t.start();
This code is in Vector4Sd.java.

The Java Virtual Machine terminates when the only threads running are daemon threads.
You can set a thread to be a daemon thread (rather than a user thread) before it starts running.


Summary of Programs


Next topic: GridBagLayout