No self-respecting developer wants to spend his or her talents and precious time writing from scratch a linked-list or queue data structure, nor a simple sort or search routine to operate on the former. In this context, Sun made a big mistake by not including a container framework in the JDK beginning with version 1.0. Fortunately, ObjectSpace Inc. offered JGL -- The Generic Collection Library for Java (formerly known as the Java Generic Library). JGL gave those who refused to reinvent the wheel an extremely powerful framework for data structures and algorithms; a framework that made those who took the time to understand its philosophy more productive than those who didn't.
JavaWorld's own recent Collections-versus-JGL poll generated passionate responses from supporters of both frameworks. By now, JGL is an old and faithful friend to many Java developers (and my integrity as a reporter demands that I disclose my membership in this group), so it didn't come as a surprise that the new kid on the block, Collections, was greeted with mixed feelings -- at least by the majority of respondents to the poll.
In this article I attempt to compare the two frameworks objectively, without letting adrenaline (or testosterone) affect my evaluation. For the record, my personal goal for this comparison exercise was to determine the true value of Sun's Collections Framework. Before porting my JGL-based code to Collections, I need to know whether the hardly negligible resources spent on porting will be a worthwhile, long-term investment. I suspect many of you are considering the same question, and hope this comparison will help you as much as it has helped me.
This article does not provide an introduction to ObjectSpace's JGL nor Sun's Collections, although the fundamental nature of these libraries should mean that most readers will be able to follow the material and discussion that will follow. Refer to JavaWorld's previous articles on JGL and Collections for detailed introductions. (See the Resources section at the end of this article for links.)
In the course of this article, I will present some tables (in the same style as those in my comparative book reviews) that will necessarily have framework A on the right, and framework B on the left. After careful consideration, I've chosen to put JGL on the left and Collections on the right of all side-by-side tables -- mainly because JGL, in most instances, provides more features than Collections. This ordering does not imply that this comparison is a guide-in-camouflage to porting from JGL to Collections (nor that porting from Collections to JGL is an unreasonable option to consider).
The versions of the libraries compared are:
- JGL version 3.0.2 (from now on, simply called JGL)
- Collections from JDK 1.2 Beta 4 (from now on, simply called Collections)
(Although I -- and JavaWorld -- normally frown upon comparisons between unfinished and finished products, Sun assured me that the Collections API is highly unlikely to change in any significant way from beta 4 to its final release, so the advanced beta status for Collections should be irrelevant. Only the implementation, which lies thoroughly behind the API facade, may change between now and the final release of the Java 2 platform.)
The immediate, high-level difference between Collections and JGL appears to be one of philosophies. Sun and ObjectSpace have different outlooks, and offer us solutions that reflect their distinct perspectives.
JGL is a direct descendant of STL, C++'s Standard Template Library. (The major STL/JGL difference: STL is hard to exploit properly, while exploiting JGL is a breeze. This difference has little to do with the libraries themselves, but instead reflects the difference in ease of use of the two targeted languages: C++ and Java.) JGL inherits STL's fundamental library architecture, as depicted by Figure 1 below.
Three of the four JGL pillars -- containers, algorithms and iterators -- are the core concepts of the "generic programming" paradigm (the brainchild of Alexander Stepanov and Meng Lee). Hence, the philosophy behind JGL is that of generic programming. (If you're unfamiliar with this paradigm, you really owe it to yourself to explore generic programming in depth. The insights you'll gain from the exercise will stay with you for the rest of your career; they're as profound as those gained from discovering design patterns. See Resources at end of article.)
With Collections, the name of the framework itself is tell-tale: Collections focuses mainly on containers, rather than on containers and algorithms. This is a very different approach from JGL, which treats algorithms and containers as equals. This fundamental difference leads to more differences down the line, eventually diverging into large areas where the two frameworks have little in common, as we shall see later.
The Collections Framework also is discussed using a language that differs from that used in JGL/STL circles: When studying Sun's Collections documentation, the main focus seems to be that of "interfaces" and "framework." No mention is made of generic programming. Instead, Sun concentrates on another commendable technique: the rigid separation of the interface from multiple, possibly competing, implementations thereof. Hence Sun's frequent accentuation of the word framework (an open, and often mainly abstract, architecture or design that third parties can implement). Although the Collections Framework does come with an implementation of the framework itself, Collections is primarily a set of Java interfaces for container data structures.
Comparing JGL and Collections
This comparison will focus on the following key issues:
- Package organization
- Interface hierarchies and APIs
- Container implementations
- Major and minor differences
Without further delay, let's look under those hoods.
External packaging may not be a reliable guide to the contents of a book, but in the world of class libraries, packaging -- in the logical sense, by means of Java package hierarchies -- is an important indicator of quality and the amount of thought that went into a library.
Table 1 compares the package approach for both frameworks.
Table 1. Package hierarchy and package names for JGL and Collections
(Note that I've omitted ObjectSpace's Voyager™ support packages: com.objectspace.jgl.voyager and com.objectspace.jgl.voyager.algorithms. While ObjectSpace may think these packages have a logical place in the jgl.* hierarchy, I disagree.)
As you can see, the JGL approach to package organization is fine-grained. JGL's six-package approach is an absolute necessity, because the total number of JGL classes and interfaces would simply become a confusing blur if stored in less packages. (See Table 2 for package statistics.) Sun elected to store the Collections Framework in the venerable
java.util package (I think a new package called
java.util.collections would have been a more sensible choice.)
Table 2: Package statistics
* Excludes java.util interfaces Enumeration, EventListener and Observer, since these are not part of the Collections Framework.
** Excludes pre-Collections classes like Vector, Stack, Bitset.
*** Includes java.lang.UnsupportedOperationException
The order-of-magnitude difference in the number of classes (151 versus 13) seems, at first sight, to be a major advantage for Collections (fewer classes means a shorter learning curve). The numbers are deceptive, though: JGL's classes are organized in very clean groups that one can mentally keep apart quite easily. A more informative presentation of the same numbers can be found in Table 3, which shows the statistics for each of JGL's subpackages.
Table 3:Subpackage statistics
JGL's (initially) daunting size ("150-plus classes") effectively deflates upon closer examination:
All 29 classes within the
jgl.adapterspackage are trivial. They are adapter classes that enable you to turn native Java arrays into JGL containers. Having learned how to use one adapter class, you'll know how to use all others.
All 22 classes within the
jgl.algorithmspackage contain only
staticmethods (the algorithms themselves). These classes are therefore as easy to use as
java.lang.Math. Additionally, the names of 18 of the 22 classes in
jgl.algorithmsend in -ing (
Replacing, and so on), further aiding mental compartmentalization.
- The 71 function and predicate classes in packages
jgl.predicatesare optional algorithm "plug-ins." As with most plug-in architectures (such as those found in Web browsers), these classes tremendously boost the flexibility of the entity being plugged into (JGL in this case). Since these plug-in objects are entirely optional, you can initially ignore them completely.
Within Collections, the following class-naming conventions help flatten its already-gentle learning curve:
Abstract implementations of the Collections interfaces start with Abstract-.
- Concrete implementations of the Collections interfaces end with the name of the implemented interface (
Mapinterface; and so on).
Having surveyed the package structure of both frameworks, let's now look at the most important facet shared by both frameworks: their respective container interface hierarchies.
Container interface hierarchies
Both frameworks have at their hearts a set of Java interfaces for their containers and iterators. First, we shall look at the container interfaces, since these are the most important. Figures 2 and 3 depict the JGL and Collections container interface hierarchies, respectively.
Comparing the two interface inheritance hierarchies, the immediate surprise is that the Collections container interface hierarchy does not define a single super-interface for the entire framework, like JGL does, but defines two interfaces:
In the Collections Framework, maps are not regarded as collections of objects, whereas JGL does regard maps as collections of objects (key-value pairs). In view of this, the name "Collections Framework" is a bit misleading. A more accurate name would be "Collections and Maps Framework." The important side-effect of not having a single root interface for the entire Collections Framework is that developers will, on occasion, slam into the immovable divide between
Maps (but see
values() for possible ways round this problem.)
If we restrict our interface hierarchy analysis to the branches rooted in JGL's
Container and the Collections Framework's
Collection (that is, if we ignore the Collections Framework's
Maps), we see that the two frameworks are actually very similar: a JGL
Sequence is the logical equivalent to a Collections
List, and a JGL
Set is equivalent to a Collections
Lists are abstract data structures that allow indexed access to their contained elements.
Sets generally are regarded as orderless containers (like an everyday "bag," for example). Container frameworks, in general, differ as to whether sets may or may not contain element duplicates. Collections rigidly enforces the no-duplicates approach, while JGL defaults to no-duplicates sets. (JGL does, however, allow duplicates in sets through an overriding mechanism.)
Container interface APIs
The container interface, JGL's
Container interface and Collections's
Collection interface, is the most important interface in each framework. These interfaces determine each framework's basic container capabilities and limitations, so we need to look at them in some detail. Listings 1 and 2 are the source code for interfaces