(Updated January 12, 2025)
Table of contents
-
Overview
Autoboxing and Auto-Unboxing
The
Vector
ClassThe
ArrayList
ClassEnumerations
Color
Vector
Part IIThe
instanceof
OperatorQuiz
Exercises
Overview
The use of arrays has opened up many possibilities for collecting related data together. This is just the beginning of collecting data of varying forms into containers of varying shapes and sizes. The array is a valuable and productive tool. Still, it is also limited because we cannot directly increase the length of an already instantiated array object. (Yes, we can create a longer array and then copy the contents to the new array as well pushing and pull the data within the array (see Chapter 9 – Managing Arrays.)
Enter the Vector
. A Vector
is essentially an array of objects with the same limitations as any other array form. The Vector
, however, takes the array to the next level; that is, it provides an intrinsic mechanism to extend the array when there is no longer any space to hold additional data. There are also mechanisms to shrink the space and insert and remove objects from the middle of the array, closing the gap made by the removal. (Again, see Chapter 9 – Managing Arrays.)
However, before we begin working with vectors, we need to revisit the wrapper classes and discuss some details that have been tucked away for a while.
Autoboxing and Auto-Unboxing
The Java language changed the wrapper class handling, starting with JDK 5.0. (See Chapter 3 wrapper classes.)
Autoboxing is the act of Java automatically calling on the appropriate constructor and methods when a primitive type is used where an object is expected. The compiler and runtime automatically compensate when there is a compatible wrapper class.
Auto-unboxing is the extraction of the primitive data from the object (i.e., intValue()
) so that it can be used in simple expressions where primitive data is expected.
This is demonstrated in Example 1.
public class Boxing {
public static void main(String[] args) {
int x, y;
Integer xobj, yobj;
// Simple primitive types
x = 10;
y = 15;
// int vars boxed to Integer and toString() invoked.
System.out.println("x is " + x);
System.out.println("y is " + y);
System.out.println("x * y = " + x * y + "\n");
// int vars are boxed to Integer
xobj = x;
yobj = y;
// toString() invoked
System.out.println("xobj is " + xobj);
System.out.println("yobj is " + yobj);
// intValue() invoked. * op has precedence.
System.out.println("xobj * yobj = " + xobj * yobj + "\n");
}
}
Example 1: Program demonstrating autoboxing and auto-unboxing.
You have witnessed autoboxing whenever you use the PrintWriter
methods print()
, println()
and printf()
. Consider the following:
System.out.println("The value of x is " + x + " and the value of y is " + y + ".");
The Java environment, in order to mix String
with int
was required to wrap the int
s making them Integer
objects. At that point it can call the toString()
method to generate the String
text necessary to mix with the other strings.
This is also the reason that the following piece of code produces something slightly unexpected:
int x = 10, y = 15;
System.out.println("The sum of x and y is " + x + y);
The output will be:
The sum of x and y is 1015
Now, seasoned programmers will spot the reason immediately. Did you spot it?
The reason this happens is:
- First, the
+
operator is evaluated from left to right. - Second, the compiler assumes we are concatenating since the first
+
is withString
andint
. - Third, the compiler further extends this to the second
+
since it is still going to beString
andint
.
Vector
and ArrayList
. These classes have a lot in common but are not perfect replacements for each other. This is because they both implement the List
interface but also have methods defined outside of that interface.
Implementing the List
interface allows for easy substitution of one class for another as long as we use the interface’s methods.
This is the underpinning of understanding Inheritance and Polymorphism. Interfaces and subclasses introduce capabilities that allow objects to take many forms. After reading through this chapter, be sure to check out the final topic The instanceof
Operator.
The Vector
Class
As we mentioned, the Vector
provides a more complete, dynamic approach to arrays. This is achievable since, under the hood, we are dealing with an array of objects. Objects imply references, which are essentially addresses. This further implies that we would be manipulating lists of references, not the data itself. In other words, if we were to add an item to or remove an item from the Vector
, we are not rearranging the data, only the references to the data.
Recall that with arrays, we tend (but are not required) to populate the contents from beginning to end. And given enough programming structure, there would be nothing (except a lack of space!) to stop us from inserting or deleting anywhere in the array. (Seriously, make sure you read Chapter 9 – Managing Arrays.)
In addition, we needed to keep track of how many values were used in conjunction with the array’s length. This would be necessary if we wanted to know when to enlarge the array. You should be able to make the leap at this point that such an action to enlarge an array would require us to review our knowledge of array copy methods, specifically those involving creating a larger array at the same time or by consciously creating a larger array first and then performing a copy. (And that is just more work!)
Constructors and methods of the Vector class. |
public Vector() Create a vector of length 10 and no defined increment. |
public Vector(int length) Create a vector of specified length and no defined increment. |
public Vector(int length, int increment) Create a vector of specified length and increase length by increment when needed. |
public boolean add(E e) Add an object at the end of the Vector. |
public void add(int index, E e) Add an object at the index . |
public int capacity() Returns the current max capacity of the vector. |
public Object clone() Returns a copy of the vector. |
public boolean contains(Object obj) Returns true if obj is within the vector, otherwise returns false . |
public E get(int index) Returns element at index . Throws ArrayIndexOutOfBoundsException . |
public int indexOf(Object obj) Returns first occurrence of obj or -1 if not found. |
public int indexOf(Object obj, int index) Returns the first occurrence of obj starting at index or -1 if not found. |
public boolean isEmpty() Returns true if the vector is empty or false if it is not. |
public int lastIndexOf(Object obj) Returns last occurrence of obj or -1 if not found. |
public int lastIndexOf(Object obj, int index) Returns last occurrence of obj starting at index or -1 if not found. |
public boolean remove(Object obj) If exists, removes obj from vector and returns true ; otherwise returns false . |
public E remove(int index) If element at index exists, it is removed. Throws ArrayIndexOutOfBoundsException . |
public void removeAllElements() Removes all elements from the vector. |
public void setElementAt(Object obj, int index) Sets the value of the element at index to obj . Throws ArrayIndexOutOfBoundException . |
public int size() Returns the number of elements contained in the vector. |
public String toString() Returns a string representation of the the vector. |
Table 1: Various members of the Vector
class.
Table 1 shows some basic members of the Vector
class. Let us look at how to declare a variable of the Vector
class:
Vector<String> rainbow = new Vector<>();
This statement declares a Vector
reference variable called rainbow
. It instantiates a new Vector
object of 10 elements and assigns it to rainbow
.
It is recommended that you include the reference type in angle brackets (< >
) as demonstrated above since we intend to store String
s in the Vector
. If you do not indicate this, you will receive a warning similar to:
Note: Program.java uses unchecked or unsafe operations. Note: Recompile with Xlint:unchecked for details.
The main concern is that the compiler will not check your objects as they are applied to certain methods. As a result, it would be possible to add an Integer
object into the vector by simply writing:
rainbow.add(56);
And as we know, the autoboxing of 56 would turn it into an Integer
object and would joyfully be added to the vector – not at all what we would like to have happen to our Vector
of String
data. With the reference type included, we would then receive a message similar to:
addElement(java.lang.String) in java.util.Vector<java.lang.String> cannot be applied to (int)
The Vector
class is contained in the java.util
package and the program in Example 2 demonstrates some basic actions on Vector
s.
import java.util.Vector;
public class ColorVector {
public static void main(String[] args) {
Vector rainbow = new Vector<>();
int x, len;
rainbow.add("Orange");
rainbow.add("Yellow");
System.out.println(rainbow);
// forgot about Red!
rainbow.add(0, "Red");
System.out.println(rainbow);
rainbow.add("Blue");
rainbow.add("Fuchsia");
rainbow.add("Indigo");
rainbow.add("Violet");
System.out.println(rainbow);
// whoops, no Fuchsia in the rainbow
rainbow.remove("Fuchsia");
// forgot about Green!
rainbow.add(3, "Green");
System.out.println(rainbow);
System.out.println();
len = rainbow.size();
for (x = 0; x < len; x++)
System.out.println("Value at index " + x + " is " + rainbow.get(x));
System.out.println();
for (String s : rainbow)
System.out.println(s);
}
}
Example 2: Program demonstrating Vectors using the rainbow colors.
In Example 2, we use our previous declaration of rainbow
and begin to add some elements to the Vector
using the add()
method. The program proceeds to alternate adding and printing the contents of the vector with the implied use of the toString()
method, which is called whenever an object is to be displayed. (For a refresher see toString()
in Chapter 8!)
The use of an overloaded version of add()
and remove()
was also employed to show the flexibility of vectors.
It is essential to see how the Vector
automatically makes a hole for values inserted into the middle of the Vector
and closes the gap left by a value that has been removed. In this example, we have even seen adding to the front, which pushes all of the existing values down one position.
A sample run of the program in Example 2 is shown below:
[Orange, Yellow] [Red, Orange, Yellow] [Red, Orange, Yellow, Blue, Fuchsia, Indigo, Violet] [Red, Orange, Yellow, Green, Blue, Indigo, Violet] Value at index 0 is Red Value at index 1 is Orange Value at index 2 is Yellow Value at index 3 is Green Value at index 4 is Blue Value at index 5 is Indigo Value at index 6 is Violet Red Orange Yellow Green Blue Indigo Violet
The ArrayList
Class
The ArrayList
class is an alternative to Vector
. It is often chosen due to lower overhead. The Vector
is synchronized. That is, it is suitable for a multi-threaded application with multiple accessors. The synchronized nature is an attempt at guaranteeing that only one thread can modify an element. Uncontrolled modifications by multiple threads could spell certain disaster. When you know for certain that only one entity will use the data, ArrayList
should be chosen over Vector
.
Constructors and methods of the ArrayList class. |
public ArrayList() Create an ArrayList of length 10 and no defined increment. |
public ArrayList(int length) Create a vector of specified length and no defined increment. |
public int size() Returns the current number of objects in the ArrayList . |
public boolean add(E e) Add an object at the end of the ArrayList . |
public void add(int index, E e) Add an object at index . |
public int indexOf(Object obj) Returns first occurrence of obj or -1 if not found. |
public E get(int index) Returns element at index . Throws ArrayIndexOutOfBoundsException . |
public boolean remove(Object obj) If exists, removes obj from ArrayList and returns true ; otherwise returns false . |
public E remove(int index) If element at index exists, it is removed. Throws ArrayIndexOutOfBoundsException . |
public void removeAllElements() Removes all elements from the ArrayList . |
public void ensureCapacity(int minSize) Guarantee capacity of at least minSize . |
Table 2: Various members of the ArrayList
class.
As you can see there are many methods of the ArrayList
class that are also in the Vector
class. This is due to the implementation of the List
interface by both classes. It is generally an easy transition from one to the other if you use methods defined in the List
interface.
One significant difference would be in the automatic growth of the object. Vector
allows you to specify the automatic growth size through a constructor. ArrayList
requires you to use the ensureCapacity()
method if you want to get beyond the automatic growth of one (1).
Take the program in Example 1 and change the decalraion using Vector
to ArrayList
as shown below:
ArrayList<String> rainbow = new ArrayList<>();
The program still produces the same result. It just uses a different class.
Enumerations
Consider our set of primitive data types such as int
, float
, char
, and double
. These data types have values and operations that can be applied to them. We have also created more robust data types called classes. These classes used the primitive data types and other classes to make something more efficient and useful than a simple integer.
Java allows us to create data types and we can specify the exact set of values that the data type may possess. The data types are called enumerations and are identified by the use of the enum
reserved word. A few examples of enumerations are shown below:
public enum Suit {Spades, Hearts, Clubs, Diamonds};
public enum Rank {Two, Three, Four, Five, Six, Seven, Eight,
Nine, Ten, Jack, Queen, King, Ace};
Each value shown in the braces is an identifier, and each value is considered an enumeration constant. In addition, each enum
is a special type of class, and each enumeration constant of this class are public static
reference variables to objects of the enum
type.
In other words, Spades
is a reference variable of type Suit
, and King
is a reference variable of type Rank
since both Suit
and Rank
are classes. So we can create declarations like the following:
Suit cardSuit;
Rank cardRank;
cardSuit = Suit.Hearts;
cardRank = Rank.King;
System.out.println(cardRank + " of " + cardSuit);
Since each enumeration constant is a reference variable and a public static
member of the enumeration class, we can access the values with the dot operator, which assigned the object Hearts
to cardSuit
and King
to cardRank
. The output of the above is:
King of Hearts
Each enumeration constant in the enumeration type has a specific value. This is known as the ordinal value, and all ordinal values are implicitly final
. The first enumeration constant is assigned the ordinal value 0, the next is 1, and so on. So for our Suit enumeration, Spades is 0, Hearts is 1, Clubs is 2, and Diamonds is 3.
Methods available for the enum type. |
public final int compareTo(E obj) Returns a negative, zero or positive value base on the ordinal of this object being less than, equal to or greater than the ordinal of obj , respectively. |
public final boolean equals(Object obj) Returns true if obj is equal to this enum constant. |
public final int ordinal() Returns the ordinal value of this enum constant. |
public final String name() Returns the name of this enum constant as declared. |
public String toString() Returns the string representation of an enum constant. |
public static E[] values() Automatically generated, implicitly declared method to return an array containing the constants of this enum . |
public static E valueOf(String) Automatically generated, implicitly declared method to return the enum constant with the specified name. |
Table 3: Enumeration type methods.
Some enumeration methods are shown in Table 3. The following nested foreach loops show one method of iterating through the suits and ranks of a deck of cards:
for (Suit suit : Suit.values())
for (Rank rank : Rank.values())
System.out.println(rank + " of " + suit);
The variable suit
is of enum
type Suit
and the values()
method of the enum
type is used to get the list of values to use in the foreach
loop. Similarly, the rank
variable is of enum
type Rank
, and the values()
method is used to feed the inner for each loop.
As we stated earlier, an enum
type is a special type of class, and the enum
constants are reference variables to the objects of that enum
type. We can immediately perceive data members, constructors, and methods within the enum
type.
Therefore, let us set up some ground rules for the enum
type:
- Enumeration types are defined with
enum
and notclass
. enum
types are implicitlyfinal
sinceenum
constants should not be modified.enum
constants are implicitlystatic
.- You may declare reference variables of the
enum
type, but you cannot instantiate objects using thenew
operator. - Constructors are implicitly
private
.
In Example 3, we have an enumeration of United States coins: penny, nickel, dime, quarter, and half-dollar. Our main()
method uses the Coins
enum
type as the reference type for Vector
to create a pocket of between 1 and 10 random coins. Each coin in the pocket is randomized.
import java.util.Vector;
public class USCoins {
public enum Coins {
Penny(0.01), Nickel(0.05), Dime(0.10), Quarter(0.25),
HalfDollar(0.50);
private final double value;
/**
* Contructor!
*
* @param value double value to be assigned to the coin
*/
private Coins(double value) {
this.value = value;
}
/**
* Get the value of the coin.
*
* @return double value
*/
public double getValue() {
return value;
}
}
public static void main(String[] args) {
// Our pocket is a vector rather than an array.
Vector pocket = new Vector<>();
int x, numCoins, randomCoin;
// values() returns the enumeration as an array
Coins[] definedCoins = Coins.values();
double pocketValue = 0.0;
// from 1 to 10 random coins in our pocket.
numCoins = (int) (Math.random() * 10 + 1);
System.out.printf("There are %d coins in our pocket.%n", numCoins);
// add a random coin
for (x = 0; x < numCoins; x++) {
randomCoin = (int) (Math.random() * definedCoins.length);
pocket.add(definedCoins[randomCoin]);
}
// Display the list of coins and calculate the pocket value
System.out.println("Our pocket contains:\n" + pocket);
for (Coins coin : pocket)
pocketValue = pocketValue + coin.getValue();
System.out.printf("%nPocket value is $%.2f.%n%n", pocketValue);
}
}
Example 3: Program demonstrating enum
type as a special class using U.S. coins.
The enumeration is designed to provide information about 5 possible coins whose names and values are clearly fixed and should never be changed. For example, a nickel is always called a nickel and is always worth 5 cents or .05 of one dollar. The enumeration type called Coins
defines 5 enumeration constants: Penny
, Nickel
, Dime
, Quarter
and HalfDollar
.
Recall from our discussion of user-defined classes that if no constructor is specified, then a default constructor is provided for you, and all instance variables are initialized to their type defaults. Since our Suit
and Rank
enumeration types had no such definitions, there were no instance variables to initialize, and there was a default constructor provided for the special enumeration type.
The stating of each enumeration constant (Penny
, Nickel
, etc.) is a call to a constructor (default or user-provided), which creates that constant as a final reference variable of an object of that enumeration type.
So Penny(0.01)
is a call to the Coin(double value)
constructor which creates Penny
as a reference variable that points to an object of enum
type Coins
whose instance variable value
is 0.01
. The same thing is true for Nickel
, Dime
, Quarter
, and HalfDollar
for their respective values.
The main()
method creates the pocket
Vector
, randomizes the number of coins, then for each coin to be placed in the pocket
, picks a random coin from the list of possible coins and puts it into the pocket with add()
.
After the Vector
is filled, we then display the coins in pocket
, sum the values of the coins with a for each loop, and display the total. A few sample runs are shown below:
There are 10 coins in our pocket. Our pocket contains: [Nickel, Dime, Quarter, Penny, Penny, Penny, Nickel, HalfDollar, HalfDollar, Dime] Pocket value is $1.58.
There are 5 coins in our pocket. Our pocket contains: [HalfDollar, HalfDollar, Dime, HalfDollar, HalfDollar] Pocket value is $2.10.
There are 4 coins in our pocket. Our pocket contains: [Dime, Nickel, Nickel, Quarter] Pocket value is $0.45.
Color Vector
Part II
In the following example, we modify the ColorVector
class from earlier to use an enumeration.
import java.util.Vector;
public class ColorVector2 {
enum Color {Red, Orange, Yellow, Green, Blue, Indigo, Violet}
public static void main(String[] args) {
Vector<Color> rainbow = new Vector<>();
int x, len;
rainbow.add(Color.Orange);
rainbow.add(Color.Yellow);
System.out.println(rainbow);
// forgot about Red!
rainbow.add(0, Color.Red);
System.out.println(rainbow);
rainbow.add(Color.Blue);
rainbow.add(Color.Indigo);
rainbow.add(Color.Violet);
System.out.println(rainbow);
// forgot about Green!
rainbow.add(3, Color.Green);
System.out.println(rainbow);
System.out.println();
len = rainbow.size();
for (x = 0; x < len; x++)
System.out.println("Value at index " + x + " is " + rainbow.get(x));
System.out.println();
for (String s : rainbow)
System.out.println(s);
}
}
Example 4: Color Vector
using an enumeration.
In the previous version, we jokingly added Fuchsia and then removed it. In this example, we cannot do that since the Vector
is now restricted to the newly introduced Color
enumeration. If it does not appear in the list defined by Color
, we cannot use it.
The great thing about this is it reduces and enforces the domain of values. With String
as our data type, we could have put any color or type of potato or favorite TV shows in the Vector
, and the compiler and runtime would allow it. Type-checking the Vector (<String>
) made sure we could not mix data types in the Vector
. Now, we have strict control over how the data looks.
The instanceof
Operator
When working with various types, we often find situations where we must be more generic about handling data. This can be done in many ways, but we will introduce just a few here.
import java.util.Vector;
import java.util.ArrayList;
import java.util.List;
public class VectorObject {
// A handful of enumerations
public enum Coins {Penny, Nickel, Dime;}
public enum Trees {Maple, Birch, Ash, Rowan;}
public enum IceCream {RockyRoad, Vanilla, CrumbsAlongTheMohawk;}
public static void main(String[] args) {
List<Object> l;
Vector<Object> v = new Vector<>();
// l references the Vector. Although List is an interface,
// it can be used to reference objects that implement it.
l = v;
// testing the instance type through polymorphism.
if (l instanceof Vector)
System.out.println("Vector");
else if (l instanceof ArrayList)
System.out.println("ArrayList");
// We are invoking the add() method of the Vector through
// late binding.
l.add(Coins.Penny);
l.add(Trees.Maple);
l.add(IceCream.Vanilla);
// Print the vector to show changes made with l.
System.out.println(v);
// Classify objects
for (Object o : v)
if (o instanceof Coins)
System.out.println("The coin is " + o);
else if (o instanceof Trees)
System.out.println("The tree is " + o);
else
System.out.println("No idea!");
}
}
Example 5a: Using List
interface to demonstrate polymorphism and instanceof
.
The output of Example 5a is below.
Vector [Penny, Maple, Vanilla] The coin is Penny The tree is Maple No idea!
In the next chapter, we will provide more detail on inheritance and polymorphism, but for now, we are content to demonstrate some of the versatility this provides. The details of the code provided in Example 5a are noted below.
- We declared variables of type
List
andVector
(which implementsList
). A newVector
object is assigned tov
. - We show how the
List
reference variable,l
, can point to an instance of a class that implementsList
. - We demonstrate using
instanceof
to determine the type ofList
pointed to byl
. - We use
l
to populate theVector
stored inv
. This is possible due to late binding and invoking the correct method when needed. instanceof to determine the kinds of objects in the Vector
.
The flexibility of using an interface reference variable to point to various objects is particularly convenient. This is further demonstrated in Example 5b, where we use a method that uses a List
parameter to allow for various types to be passed to it and take appropriate action based on the referenced type.
import java.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
public class MethodObject {
/**
* Check the type of List
*
* @param list unbounded List type
*/
public static void checkType(List> list) {
// use instanceof to determine type.
if (list instanceof Vector)
System.out.print("Vector has size " + list.size() + " and capacity " + ((Vector) list).capacity());
else if (list instanceof ArrayList)
System.out.print("ArrayList has size " + list.size() + " and unknown capacity");
else
System.out.print("Cannot use " + list.getClass());
System.out.println(" with contents " + list);
}
public static void main(String[] args) {
Vector v = new Vector<>(20);
ArrayList a = new ArrayList<>(20);
LinkedList l = new LinkedList<>();
v.add(36);
v.add(42);
checkType(v);
a.add(36);
a.add(42);
checkType(a);
l.add(36);
l.add(42);
checkType(l);
}
}
Example 5b: Using an interface on a method to
Vector has size 2 and capacity 20 with contents [36, 42] ArrayList has size 2 and unknown capacity with contents [36, 42] Cannot use class java.util.LinkedList with contents [36, 42]
In Example 5b, there are some specific details worth mentioning.
checkType()
shows an argument of typeList<?>
, which is an unbounded wildcard type (see Wildcards in Chapter 11.)- We determine if the instance is a
Vector
. If true, it also uses a cast to be able to invoke thecapacity()
method, whichArrayList
does not have. - We determine if the instance is an
ArrayList
. If true, it is only able to invokesize()
sincecapacity()
is not available. - We even printed the class we got and were unwilling to use. The
getClass()
method returns theString
representation of the object pointed to bylist
. List contents to the output immediately after the other details are determined. - In
main()
, we create three different objects that implementList
.