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.
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.
|
COM.objectspace
with com.objectspace
is usually enough to get things working again.
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.
com.objectspace.jgl
contains the interfaces, collection classes, and iterators.com.objectspace.jgl.adapters
contains the Java native array adapters.com.objectspace.jgl.algorithms
contains all the JGL algorithms.com.objectspace.jgl.functions
contains function objects that can be used to modify algorithms.com.objectspace.jgl.predicates
contains predicate classes used to specify element ordering within containers.com.objectspace.jgl.util
contains a few miscellaneous utility classes.com.objectspace.jgl.voyager
contains a version of the collection classes that can be used in a distributed fashion with ObjectSpace Voyager.com.objectspace.jgl.voyager.algorithms
contains distributed algorithms for use with ObjectSpace Voyager.JGL comes complete with the following:
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.
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.
// 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.
// 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).
// 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.
// 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.
// 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
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.
// 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.
// 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.
// 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).
// 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 )
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.
// 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.
// 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 )
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.
// 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
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 |
// 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 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.