Working with Generics and Collections in Java
Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.
The Java Programming Language is basically one that supports both static and dynamic types. When working with type safe languages such as Java, an object should know its type before a value that matches its type is assigned to it. On the contrary, generics (also known as parameterized types) enable you to have deferred instantiation of types; these generic types are bound to an exact type only at runtime.
This article explores Generics and Collections in the Java programming language with code examples wherever appropriate.
What Are Arrays and What Are the Constraints in Using Them?
An array may be defined as a collection of elements of homogenous types. Note that arrays in Java have certain constraints; these include:
- The size of an Array is fixed; it is incapable of growing or shrinking in size dynamically
- Type safety is very limited
We will examine how generics can be a better choice when designing type safe reusable collections in Java. By using generics, you can develop generic or universal classes and methods that can accept any type as a parameter, thereby promoting reusability, efficiency, and maintainability of code.
Hang on. Let’s take a quick tour of the collections framework in Java before we delve deep into what generics has in store for us.
The Java Collections Framework
A collection is essentially a group of objects of a similar type. The collection framework in Java (commonly known as the Java Collection Framework) is comprised of a collection of types that enable you to implement complex data structures seamlessly. The collection interfaces in the Java Collections Framework include: Collection, List, Set, Map, and so on. And, the major classes in this framework are: ArrayList, LinkedList, HashSet, HashMap, and the like. The Java Collections Framework is contained inside the java.util.Collection package.
To iterate over a collection, we need an iterator. The java.util.Iterator package contains methods such as hasNext(), next(), and remove(). The hasNext() method returns true if there are more elements in the collection. The next() method returns the next element in the collection. The remove() method removes the last element from a collection.
The following method illustrates how you can display all elements in a collection in Java.
private static void DisplayElements(Collection c) < Iterator iterator = c.iterator(); while (iterator.hasNext()) < Object obj = iterator.next(); System.out.println(obj); >>
The What and Why of Generics in Java
Generics is a feature of the Java Language that promotes type safety, code manageability and efficiency and, most importantly, much cleaner and reduced code. You can use generics to create type safe collections with no boxing and un-boxing overhead; this is also known as parametric polymorphism.
Implementing a Custom Stack Class in Java
We have had enough of the concepts; let’s write some code now. We will implement a custom stack class in Java. In the first phase, we will have a custom stack class that doesn’t use generic types. In the modified version, we will see how a custom stack class can be designed and the benefits of doing so.
Here is what a custom stack class (sans generic types) looks like:
public class CustomStack < //Pre-defined size of the stack private static final int size = 10; //This is an array that contains the elements //of the stack private Object[] register; private int count = 0; private CustomStack() < register = new Object[size]; >public final void Push(Object x) < if (count < size) < register[count++] = x; >> public final Object Pop() < return register[--count]; >static void main(String[] args) < CustomStack integerStack = new CustomStack(); integerStack.Push(10); int i = (Integer)intStack.Pop(); System.out.println(i); System.in.read(); >>
So, what did we do in the preceding code? We implemented a custom stack class that can be used to push and pop elements (insert and delete elements). Now, the limitation of this type of an implementation is that you are constrained to use one type at a given point of time; you can use the custom stack class to insert or delete integers or strings or objects of a particular type. So, every time you need a custom stack class for a particular type, you need to have a custom stack class implemented for that type. Sounds weird, right? Here’s exactly where generics fits in. How? Patience, please!
Here’s why we would leverage the awesome power of generics. The modified CustomStack class is given below.
public class CustomStack < private static final int size = 10; private S[] register; private int count = 0; public CustomStack() < register = new S[size]; >public final void Push(S x) < if (count < size) < register[count++] = x; >> public final S Pop() < return register[--count]; >>
The main() method given below illustrates how you can use the custom stack class to insert or delete elements.
public class Test < static void main(String[] args) < CustomStackintegerStack = new CustomStack(); integerStack.Push(10); int i = integerStack.Pop(); System.out.println(i); System.in.read(); > >
As is evident from the preceding code, you can use the same generic custom class for any type you would want the stack to work with. Cool!
Summary
In this article, we examined the power of generics and generic collections in Java with code samples to illustrate the concepts. I will discuss more on the features of the Java Programming Language in my future articles. So, stay tuned!