ObjectSpace Homepage

JGL - The Generic Collection Library for Java
Contents Preface Containers

Overview

What is JGL™?
Changes in Version 3.x
Packages in JGL
JGL and the JDK
Containers
Algorithms
Function Objects
Iterators
ObjectSpace Voyager™
Performance

What is JGL

The JDK contains very limited support for data collections and algorithms. It has been designed to provide the minimal subset of features used by the majority of Java developers. This is sufficient for simple applet design, or other trivial use cases, but if you are embarking on any serious Java development, you need a professional package of collection and algorithms. You need JGL.

JGL was designed to not only provide you with the essential enterprise collections you need, but also advanced data processing algorithms for use with those collections. With over 50 algorithms, JGL provides the powerful code base you need for data processing. These algorithms have been carefully designed so you can use them on JGL collections, Java native arrays of primitives and objects, and JDK collections. They are generic algorithms that can be adapted using function objects and predicates to solve most collection processing problems you encounter.

Version 1 of JGL was made free to the Java community in June 1996. Since then it has been licensed by 10 major IDE vendors and downloaded by tens of thousands of users. Its estimated current user base exceeds 100,000 users. JGL was designed from the start as a 100% Java, high performance, full featured, and easy-to-use extension to the JDK.

This user guide describes the use of JGL with a mix of text and example programs. The rest of this chapter provides an overview of the main features of JGL - containers, algorithms, function objects, iterators, and the exciting new distributed system features. Subsequent chapters describe each JGL topic in detail.


Changes in Version 3.x

Version 3.1 of JGL has only one major change: the packange names have been changed to reflect the upcoming Java specification changes. Instead of the prefix COM.objectspace.jgl, com.objectspace.jgl (note the lowercase com) is used.

Version 3.0 of JGL includes two major changes.
  • The single JGL package was separated into several smaller packages. This composition optimizes your learning curve, compile times, and network class loading when using jar files. See the API documentation for a complete listing of packages and classes.
  • Two new packages have been added to JGL, enabling the use of JGL collections in distributed system development using ObjectSpace Voyager. Remotely construct collections, access them, and persist them in the Voyager Database. Voyager’s unique nonintrusive architecture there are no changes to the JGL interfaces when using JGL remotely. Simply load the new packages and go.
Several smaller changes have also been made in JGL, including the addition of a new conditional enumeration. We would like tothank our thousands of users for their feedback. We have taken care to ensure backward compatibility whenever possible. Usually the only change required to move to version 3.0 is the addition of new import lines in a class to pull in the new packages. When moving to 3.1, replacing all instances of COM.objectspace with com.objectspace is usually enough to get things working again.


Packages in JGL

JGL includes eight Java packages, each carefully designed to include a set of related functionalies. This makes JGL easy to learn, fast to compile, and fast to distribute in jar file format. Only use what you need.

The following is a list of the eight Java packages and their contents.

JGL comes complete with the following:


JGL and the JDK

The JDK contains very limited support for data collections and algorithms. It has been designed to provide the minimal subset of features used by the majority of Java developers. This is sufficient for simple applet design, or other trivial use cases, but if you are embarking on any serious Java development, you need a professional package of collection and algorithms. You need JGL.

JGL version 3 works with JDK 1.1. It has been designed to extend the JDK, not replace it.

As the JDK evolves, so will JGL. The JDK will never grow to include all the facilities needed by serious enterprise developers. It was designed to include the minimal subset of features needed by the majority of developers. JGL will track the JDK, extending its core features to meet the needs of enterprise developers. JGL 4.0 is already scheduled, and will extend JDK 1.2 to provide developers with the same level of compatibility and functionality they find today in JGL 3.0. Remember that the adoption and proliferation of the JDK is affected by the adoption of browsers and IDEs. This slows adoption dramatically. New JDK features, once released from Sun, can take over a year to proliferate to the extent that you can reliably use them across the board.


Containers

This section contains some examples of JGL containers. See the Containers chapter for more details.

JGL includes 11 highly optimized data structures that satisfy most programmers' needs. Their performance either meets or beats any other commercially available Java container library, and they have been engineered for performance and ease of use. In addition, all JGL containers work correctly in multithreaded situations.

The following example uses a JGL HashMap to associate a chemical symbol with its full name. Note that the entire container can be printed easily using the println() function.



Example Overview1.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;

public class Overview1
  {
  public static void main( String[] args )
    {
    HashMap chemicals = new HashMap();
    chemicals.put( "Ca", "Calcium" );
    chemicals.put( "Au", "Gold" );
    chemicals.put( "He", "Helium" );
    System.out.println( "chemicals = " + chemicals );
    System.out.println( "Au means " + chemicals.get( "Au" ) );
    }
  }

Output
chemicals = HashMap( Pair( Au, Gold ), Pair( He, Helium ), Pair( Ca, Calcium ) )
Au means Gold


The next example illustrates some set operations using the HashSet class. Note how simple it is to obtain the union and intersection of two sets.



Example Overview2.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;

public class Overview2
  {
  public static void main( String[] args )
    {
    HashSet set1 = new HashSet();
    set1.add( "red" );
    set1.add( "blue" );
    set1.add( "green" );

    HashSet set2 = new HashSet();
    set2.add( "yellow" );
    set2.add( "blue" );

    HashSet set3 = set1.union( set2 );
    HashSet set4 = set1.intersection( set2 );

    System.out.println( "set1 = " + set1 );
    System.out.println( "set2 = " + set2 );
    System.out.println( "union of set1 and set2 = " + set3 );
    System.out.println( "intersection of set1 and set2 = " + set4 );
    }
  }

Output
set1 = HashSet( blue, green, red )
set2 = HashSet( blue, yellow )
union of set1 and set2 = HashSet( blue, green, red, yellow )
intersection of set1 and set2 = HashSet( blue )


One of JGL's strengths is its seamless compatibility with the existing JDK utility classes. For example, you can use a standard JDK Enumeration object to iterate through every element of any JGL container. The following example uses an Enumeration to enumerate the contents of an Array (resizeable array) and a DList (doubly-linked list).



Example Overview3.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import java.util.Enumeration; // JDK enumeration class.

public class Overview3
  {
  public static void main( String[] args )
    {
    Array array = new Array(); // Resizeable array.
    array.add( "nemesis" );
    array.add( "dig" );
    array.add( "myst" );

    Enumeration iterator = array.elements();
    while ( iterator.hasMoreElements() )
      System.out.println( iterator.nextElement() );
    System.out.println();

    DList list = new DList(); // Doubly-linked list.
    list.add( "agatha" );
    list.add( "beauty" );
    list.add( "truth" );

    iterator = list.elements();
    while ( iterator.hasMoreElements() )
      System.out.println( iterator.nextElement() );
    }
  }

Output
nemesis
dig
myst

agatha
beauty
truth


One of the exciting new features of the JDK, version 1.1, is object serialization. Beginning with the JGL version 2.0 this feature is implemented in all containers. This means you can stream a container and all its contents out over a socket or into a persistent file with ease.



Example Serial1.java
// Copyright(c) 1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import java.io.*;
import java.util.*;

public class Serial1
  {
  static public void write()
    {
    try
      {
      // create a map of acronyms
      HashMap map = new HashMap();
      map.add( "FAQ", "Frequently Asked Questions" );
      map.add( "OMG", "Object Management Group" );
      map.add( "ORB", "Object Request Broker" );

      // save map to a file
      ObjectOutput s = new ObjectOutputStream( new FileOutputStream( "Serial1.bin" ) );
      s.writeObject( map );
      }
    catch ( IOException e )
      {
      System.out.println( "caught: " + e );
      }
    }

  static public void read()
    {
    try
      {
      // read map from file
      ObjectInputStream s = new ObjectInputStream( new FileInputStream( "Serial1.bin" ) );
      HashMap map = (HashMap)s.readObject();
      System.out.println( "ORB means " + map.get( "ORB" ) );
      System.out.println( "FAQ means " + map.get( "FAQ" ) );
      }
    catch ( IOException e1 )
      {
      System.out.println( "caught: " + e1 );
      }
    catch ( ClassNotFoundException e2 )
      {
      System.out.println( "caught: " + e2 );
      }
    }

  public static void main( String args[] )
    {
    write();
    read();
    }
  }

Output
ORB means Object Request Broker
FAQ means Frequently Asked Questions


For the sequential containers in JGL (Deque, SList, DList, Array, and the array adapters), iterators can be serialized as well as the containers themselves.



Example Serial2.java
// Copyright(c) 1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import java.io.*;
import java.util.*;

public class Serial2
  {
  static public void write()
    {
    try
      {
      // create a list of names
      DList names = new DList();
      names.add( "Peter Parker" );
      names.add( "Frank Castle" );
      names.add( "Logan" );
      names.add( "Steve Rogers" );

      // save names to a file
      ObjectOutput s = new ObjectOutputStream( new FileOutputStream( "Serial2.bin" ) );
      s.writeObject( names );

      // search for some particular entries
      ForwardIterator wolverine = names.find( "Logan" );
      ForwardIterator hulk = names.find( "Bruce Banner" );

      // write the iterators to the file as well
      s.writeObject( wolverine );
      s.writeObject( hulk );
      }
    catch ( IOException e )
      {
      System.out.println( "caught: " + e );
      }
    }

  static public void read()
    {
    try
      {
      // read sequence and iterator from file
      ObjectInputStream s = new ObjectInputStream( new FileInputStream( "Serial2.bin" ) );
      DList names = (DList)s.readObject();
      ForwardIterator wolverine = (ForwardIterator)s.readObject();
      ForwardIterator hulk = (ForwardIterator)s.readObject();

      // check the iterators
      if ( wolverine.equals( names.end() ) )
        System.out.println( "Don't know who Wolverine is" );
      else
        System.out.println( "Wolverine is also known as " + wolverine.get() );
      if ( hulk.equals( names.end() ) )
        System.out.println( "Don't know who the Hulk is" );
      else
        System.out.println( "Hulk is also known as " + hulk.get() );
      }
    catch ( IOException e1 )
      {
      System.out.println( "caught: " + e1 );
      }
    catch ( ClassNotFoundException e2 )
      {
      System.out.println( "caught: " + e2 );
      }
    }

  public static void main( String args[] )
    {
    write();
    read();
    }
  }

Output
Wolverine is also known as Logan
Don't know who the Hulk is


Algorithms

This section contains some examples of the power of JGL algorithms. See the Algorithms chapter for more details.

One of the main strengths of JGL is its complement of over 50 reusable algorithms that can perform tasks ranging from a simple filtering operation to a complex quicksort. Instead of embedding algorithms into containers, JGL algorithms are encapsulated within their own class and can operate upon a wide variety of data structures, including JGL containers, JDK containers, and native Java arrays. All algorithms that are closely related are encapsulated as class methods of a single class. For example, all of the sorting algorithms are methods in Sorting.

To demonstrate the power and flexibility of this approach, the next three sections apply the single JGL reusable sorting algorithm to three different kinds of data structures.



Example Overview4.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.algorithms.*;

public class Overview4
  {
  public static void main( String[] args )
    {
    Array array = new Array();
    array.add( new Integer( 3 ) );
    array.add( new Integer( -1 ) );
    array.add( new Integer( 2 ) );
    System.out.println( "Unsorted Array = " + array );
    Sorting.sort( array );
    System.out.println( "Sorted = " + array );
    }
  }

Output
Unsorted Array = Array( 3, -1, 2 )
Sorted = Array( -1, 2, 3 )


The second example in this series uses the same sorting algorithm to sort a JDK Vector. JGL supplies a simple wrapper class to make a JDK Vector look like a JGL container so that the algorithms can work directly upon it.



Example Overview5.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.adapters.*;
import com.objectspace.jgl.algorithms.*;
import java.util.Vector;

public class Overview5
  {
  public static void main( String[] args )
    {
    Vector vector = new Vector(); // JDK Vector.
    vector.addElement( new Integer( 3 ) );
    vector.addElement( new Integer( -1 ) );
    vector.addElement( new Integer( 2 ) );
    System.out.println( "Unsorted java.util.Vector = " + vector );
    // A VectorArray makes a JDK vector look like a JGL container.
    VectorArray vectorArray = new VectorArray( vector );
    Sorting.sort( vectorArray );
    System.out.println( "Sorted = " + vector );
    }
  }

Output
Unsorted java.util.Vector = [3, -1, 2]
Sorted = [-1, 2, 3]


The third and final algorithm example uses the same sorting algorithm to sort a native Java array of ints. In a similar manner to the last example, JGL supplies simple wrapper classes to make native Java arrays look like JGL containers.



Example Overview6.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.adapters.*;
import com.objectspace.jgl.algorithms.*;

public class Overview6
  {
  public static void main( String[] args )
    {
    int ints[] = { 3, -1, 2 };
    IntArray intArray = new IntArray( ints );
    System.out.println( "Unsorted native int array = " + intArray );

    Sorting.sort( intArray );
    System.out.print( "Sorted native array = " );
    for ( int i = 0; i < ints.length; i++ )
      System.out.print( ints[ i ] + " " );
    System.out.println();
    }
  }

Output
Unsorted native int array = int[]( 3, -1, 2 )
Sorted native array = -1 2 3


JGL includes many different kinds of algorithm. This last example uses the random shuffling algorithm to shuffle the contents of a Deque (double-ended queue).



Example Overview7.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.algorithms.*;

public class Overview7
  {
  public static void main( String[] args )
    {
    Deque deque = new Deque();
    deque.add( "your" );
    deque.add( "mission" );
    deque.add( "jim" );
    System.out.println( "Original deque = " + deque );
    Shuffling.randomShuffle( deque );
    System.out.println( "Shuffled deque = " + deque );
    }
  }

Output
Original deque = Deque( your, mission, jim )
Shuffled deque = Deque( mission, jim, your )


Function Objects

This section contains a few examples of JGL function objects. See the Function Objects chapter for more details.

Many algorithms can accept an optional function object to modify the way they work. A function object is an object that encapsulates a single function and can execute it on one or more arguments. JGL contains over 30 reusable function objects.

The default sorting algorithm orders elements in ascending order based on their hash code. The following example uses a GreaterNumber function object to change the sorting algorithm to order elements in descending order.



Example Overview8.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.algorithms.*;
import com.objectspace.jgl.predicates.*;

public class Overview8
  {
  public static void main( String[] args )
    {
    Array array = new Array();
    array.add( new Integer( 3 ) );
    array.add( new Integer( -1 ) );
    array.add( new Integer( 2 ) );
    System.out.println( "Unsorted Array = " + array );
    BinaryPredicate predicate = new GreaterNumber();
    Sorting.sort( array, predicate ); // Sort in descending order.
    System.out.println( "Sorted = " + array );
    }
  }

Output
Unsorted Array = Array( 3, -1, 2 )
Sorted = Array( 3, 2, -1 )


Other algorithms that can accept function objects are countIf() and removeCopyIf(). countIf() counts the elements in a container that satisfy a given condition, and removeCopyIf() copies elements that don't satisfy a condition from one container to another. Both of these algorithms are illustrated by the following example.



Example Overview9.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.predicates.*;
import com.objectspace.jgl.algorithms.*;

public class Overview9
  {
  public static void main( String[] args )
    {
    Array array1 = new Array();
    array1.add( new Integer( 3 ) );
    array1.add( new Integer( -1 ) );
    array1.add( new Integer( 2 ) );

    UnaryPredicate predicate = new PositiveNumber();
    int n = Counting.countIf( array1, predicate );
    System.out.println( "# of positive numbers in " + array1 + " = " + n );

    Array array2 = new Array();
    Removing.removeCopyIf( array1, array2, predicate );
    System.out.println( "Array without positive numbers = " + array2 );
    }
  }

Output
# of positive numbers in Array( 3, -1, 2 ) = 2
Array without positive numbers = Array( -1 )


Iterators

This section contains a brief overview of JGL iterators. See the Iterators chapter for more details.

In addition to supporting the JDK Enumeration type, JGL includes several powerful classes of iterators. For example, a ReverseIterator allows you to iterate backwards over a container. JGL also includes an iterator for stepping through an I/O stream. The following example uses a reverse iterator to enumerate the contents of an Array in reverse order.



Example Overview10.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.util.*;

public class Overview10
  {
  public static void main( String[] args )
    {
    Array array = new Array();
    array.add( "jolly" );
    array.add( "good" );
    array.add( "show" );

    // Create a reverse iterator positioned at the end of the array.
    ReverseIterator iterator = new ReverseIterator( array.end() );
    while ( iterator.hasMoreElements() )
      System.out.println( iterator.nextElement() );
    }
  }

Output
show
good
jolly


ObjectSpace Voyager™

This section contains a brief demonstration of using JGL with ObjectSpace Voyager. See the Voyager chapter for more details.

ObjectSpace Voyager is a powerful platform for creating distributed Java applications. Using Voyager with JGL gives you distributed collection support, allowing the remote construction, access, and persistence of all JGL containers. Because of Voyager's non-intrusive architecture, a JGL collection can be used remotely in the same manner as you would use it locally. The following example creates an Array in another address space, sorts it remotely, and then iterates through the remote elements.



Example Overview11.java
// Copyright(c) 1996,1997 ObjectSpace, Inc.
import com.objectspace.jgl.*;
import com.objectspace.jgl.predicates.*;
import com.objectspace.jgl.voyager.*;
import com.objectspace.jgl.voyager.algorithms.*;
import com.objectspace.voyager.*;

public class Overview11
  {
  /**
   * This example only works with ObjectSpace Voyager(tm).
   * Visit the Voyager homepage for more information.
   */
  public static void main( String[] args )
    {
    try
      {
      // Construct a new Array object on localhost:8000 and build a virtual
      // reference called array to comunicate with it. Add elements as if
      // it were local.
      VArray array = new VArray( "localhost:8000" );
      array.add( "Texas Fight!" );
      array.add( "Bevo" );
      array.add( "Hook 'Em" );

      // Persist the remote array in the Voyager Database.
      array.saveNow();

      // printing works like you'd expect
      System.out.println( "container=" + array );

      // remote algorithms
      VSorting.sort( array, new LessString(), "localhost:8000" );
      System.out.println( "sorted container=" + array );

      // and iteration as well
      array.setVirtual( true );
      new VArrayIterator(); // make sure the class is loaded
      ForwardIterator iter = array.start();
      array.setVirtual( false );
      while ( iter.hasMoreElements() )
        System.out.println( "element=" + iter.nextElement() );
      }
    catch ( VoyagerException ex )
      {
      System.err.println( "caught: " + ex );
      }

    Voyager.shutdown();
    }
  }

Output
voyager(tm) 2.0 beta 1, copyright objectspace 1997
address = 285.42.132.60:1185
container=Array( Texas Fight!, Bevo, Hook 'Em )
sorted container=Array( Bevo, Hook 'Em, Texas Fight! )
element=Bevo
element=Hook 'Em
element=Texas Fight!


Performance

Performance has been a concern for JGL from the start. In almost every case it compares well to or beats any equivalent functionality available today. Using optimized code, carefully architected package layout, and encapsulated algorithms we have optimized compile, network load, and execution times. JGL is architected to be lean and mean without sacrificing features, making it ideal for both enterprise development and Internet distribution.

Don't just take our word for it! ObjectSpace ships a suite of performance benchmarks with JGL. These benchmarks compare JGL performance with the JDK data structures.


Contents Preface Containers