http://www.developer.com/

Back to article

Objects and Collections


July 10, 2006

This series, The Object-Oriented Thought Process, is intended for someone just learning an object-oriented language and who wants to understand the basic concepts before jumping into the code, or someone who wants to understand the infrastructure behind an object-oriented language he or she is already using. These concepts are part of the foundation that any programmer will need to make the paradigm shift from procedural programming to object-oriented programming.

Click here to start at the beginning of the series.

In keeping with the code examples used in the previous articles, Java will be the language used to implement the concepts in code. One of the reasons that I like to use Java is because you can download the Java compiler for personal use at the Sun Microsystems Web site http://java.sun.com/. You can download the standard edition, J2SE 5.0, at http://java.sun.com/j2se/1.5.0/download.jsp to compile and execute these applications. I often reference the Java J2SE 5.0 API documentation and I recommend that you explore the Java API further. Code listings are provided for all examples in this article as well as figures and output (when appropriate). See the first article in this series for detailed descriptions for compiling and running all the code examples.

In the previous column, I covered the topic of primitives (basically the low-level system variables), objects, and wrappers. This discussion included all of the primitive types, as well as a brief description of how strings relate to these primitives. At the end of the article, I touched upon some of the JDK 1.5 Enhancements, specifically Autoboxing and Unboxing.

Interestingly, one of the best examples of object wrappers is provided by the JDK itself in the form of the primitive wrappers such as the classes Integer, Double, and objects. When objects are created from these classes, they literally wrap a single primitive inside that object. This creates a lot of overhead for the single primitive; however, it provides much-needed behavior to use and process the primitive. Wrapping the primitive also allows the value stored by the primitive to be used as on object. This is an issue that you will explore in more detail later.

These wrappers hold a single value. Obviously, there are times when you are interested in working with multiple values—this is where the term collections enters the discussion. In this article, you will start the exploration of how objects relate to collections. This is a direct progression from the discussion of primitives. You will start by covering arrays and will consider the advantages and disadvantages of the original concept of an array. Next, you will cover how collections such as ArrayLists provide additional functionality.

Arrays

Because this series focuses on object concepts, you will not explore the syntax of arrays in great detail; however, you will touch upon some of the interesting ways that arrays are influenced by the object-oriented paradigm and how they relate to objects.

The Sun documentation states that the Java programming language arrays are objects, are dynamically created, and may be assigned to variables of type Object. All methods of class Object may be invoked on an array.

Declaration & Instantiation

Start out with a simple piece of code (this code has a bug):

public class ArrayLists {
   public static void main(String[] args) {
      int items[];
      items[1] = 5;
      System.out.println("items[1]=" + items[5]);
   }
}

Listing 1

The syntax for creating an array is as follows:

int items[];

One of the most confusing issues of moving from C/C++ to Java involves syntax such as you have in the previous line. In this case, a C/C++ programmer would identify right away that the size of the array is missing.

A C/C++ programmer is used to creating an array in one, concise statement like this:

int items[5];

In C/C++, the memory for the array is calculated by the compiler and allocated without having the programmer explicitly creating it. Thus, the instantiation and the creation are done together.

However, in Java and other object-oriented implementations, this is not the case. The declaration and the instantiation are two distinct processes. This is why there is a bug in the following code.

int items[5];

This line contains only the declaration of the array. If you try to compile this, you receive the following error:

C:column24>"C:Program FilesJavajdk1.5.0_06binjavac" ArrayList.java
ArrayList.java:7: variable items might not have been initialized
          items[1] = 5;
          ^
1 error
C:column24>

Although this error might seem a bit cryptic, it is actually identifying the fact that the array pointer (the variable items) has yet to be instantiated. In other words, the pointer does not point at anything yet because the memory for the array has not been allocated.

To allocate the memory for the array, you must add a second part to this process.

items = new int[5];

This line identifies that the array is to consist of five integers. It makes sense that the compiler needs to know what the array will consist of and how many of these items there are. In this example, you initialize items[1] to the value 5 and then simply print it out. The code in Listing 2 does compile cleanly.

public class ArrayList {
   public static void main(String[] args) {
      int items[];
      items = new int[5];
      items[1] = 5;
      System.out.println("items[1]=" + items[1]);
   }
}

Listing 2

When this code is executed, the following output is generated (the actual program output is in bold).

C:column24>"C:Program FilesJavajdk1.5.0_06binjava" Te
items[1]=5
C:column24>

Although you have separated the declaration and the instantiation in this example by using two separate lines, it is possible, of course, to combine the two processes in the same line. In fact, this is the syntax normally seen.

int items[] = new int[5];

Initializing Arrays

Some of the more interesting array examples require that you first initialize the arrays to fully demonstrate the code. Consider the following code:

int[] items = {0,1,2,3,4};

In this line of code, you explicitly initialize the array as you declare and instantiate it. Note that there is no requirement for the new keyword. By using this syntax, you instruct the compiler to allocate the memory for the array and initialize each of the array items. Also note that there is no need to specify the size of the array; the compiler can figure that out by the number of entries within the declaration brackets.

Now, introduce a loop into this simple example and you will be ready to explore some interesting array/object connections.

public class ArrayLists {
   public static void main(String[] args) {
      int[] items = {0,1,2,3,4};
      for (int i = 0; i<5; i++) {
         System.out.println("items[" + i + "]=" + items[i]);
      }
   }
}

Listing 3

When this code is executed, the following output is generated (the actual program output is in bold).

C:column24>"C:Program FilesJavajdk1.5.0_06binjava" ArrayLists
items[0]=0
items[1]=1
items[2]=2
items[3]=3
items[4]=4
C:column24>

This code is basically a benchmark. By creating this simple example, we can use it as a baseline for everything that we do going forward. This process allows us to provide the infrastructure for all of our future examples. Perhaps more importantly, it provides a testing framework that will allow us to concentrate on the development process of our examples.

You also can create a multi-dimensional array. The syntax for a multi-dimensional array can be quite confusing. For example, create a 2-dimensional array.

int[][] items = { {0,1,2,3,4},
                  {5,6,7,8,9},
                  {10,11,12,13,14},
                  {15,16,17,18,19},
                  {20,21,22,23,24}};

Again, note that there is no need to specify the size of the array; the compiler can figure this out by simple counting. The one thing that I like to do in the case of multi-dimensional arrays is to format my code so that it is obvious where the dimensional boundaries are. In this case, the 2-dimensional array boundaries are obvious because they are grouped visually as 5 by 5. Grouping in this way can save a lot of syntax headaches.

public class ArrayLists {
   public static void main(String[] args) {
      int[][] items = { {0,1,2,3,4},
                        {5,6,7,8,9},
                        {10,11,12,13,14},
                        {15,16,17,18,19},
                        {20,21,22,23,24}};

      for (int i = 0; i<5; i++) {
         for (int j = 0; j<5; j++) {
            System.out.print(items[i][j]+ " ");
         }
         System.out.println("");
      }
   }
}

Listing 4

When this code is executed, the following output is generated (the actual program output is in bold).

C:column24>"C:Program FilesJavajdk1.5.0_06binjava" ArrayLists
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
C:column24>

Even though these types of array initializations are a great way to build testing directly into an application, there are certain applications where arrays can be used in a production situation.

String days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

In this case, where the contents of the array are unlikely to change, array initializations come in handy.

Subtle Syntax

There is one more syntax note that I would like to point out. Java introduced an alternate form. The array brackets can be attached to the type itself as well as the array namem as can be seen in the following line of code.

int[] items = new int[5];

There is an interesting and subtle difference between the two types of declarations—they are not quite the same. Consider the following two lines of code.

int[] x;
int[] y;

The array brackets are attached to the type, not the name of the array.

The following line of code is equivalent.

int[] x, y;

Because the array brackets are attached to the type, you can create multiple integer arrays simply by providing the variable names.

You also can create arrays of arrays in this manner.

int[] x, y[];

Here, you have created an integer array of integer arrays called y[][].

It is in this same way that arrays of objects can be created, as can be seen in Listing 5. I have created a class called Employee, and the application creates two Employee arrays named fullTime and partTime.

public class ArrayLists {
   public static void main(String[] args) {
      Employee [] fullTime, partTime;
   }
}
class Employee {
}

Listing 5

Here is where you delve deeper into the relationships between objects and arrays.

Object-Oriented Arrays?

How do you know that the Java array is actually an object? Well, for one thing, the Java documentation tells you that it is. Java provides an Array class.

java.lang.reflect
Class Array

java.lang.Object
   java.lang.reflect.Array

Just like everything besides the primitives, the Array class inherits from the Object class.

How else can you determine that an array is really an object? All you need to do is to relate an array to the simple definition of an object, which is that all objects have attributes and behaviors.

Look at the code where you initialized the days of the week. Listing 6 shows how you can print out the entire array.

public class ArrayLists {
   public static void main(String[] args) {
      String days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
      for (int i = 0; i<7; i++) {
         System.out.println(days[i]);
      }
   }
}

Listing 6

When this code is executed, the following output is generated (the actual program output is in bold).

C:column24>"C:Program FilesJavajdk1.5.0_06binjava" ArrayLists
Sun
Mon
Tue
Wed
Thu
Fri
Sat
C:column24>

As you may have noticed already, the size of the array has been hard-coded in the loop. This is normally not coding practice. You actually can use some of the Array class's attributes to take care of this problem.

for (int i = 0; i < days.length; i++) {
   System.out.println(days[i]);
}

By using the length attribute of the Array class, you do not have to hard-code the value. This is important because any change in the size of the array will not necessitate a change to the code. For example, if you decide that you only want to include the weekdays in the array, all you have to do is change the array.

String days[] = { "Mon", "Tue", "Wed", "Thu", "Fri"};

The code for the loop remains the same.

One interesting issue here lies in the length attribute itself. Normally, in an object-oriented design, you cannot access attributes directly. Even if you don't consider length to be an attribute, it certainly is a value. And, one of the fundamental principles of object-oriented design is that you cannot access an attribute directly. To do so breaks the concept of encapsulation and data hiding. If you were designing an object-oriented class, you would create length as a private attribute and then create a method to return the value, say getLength( ). In another point of object-oriented design, you know that methods are indicated by the following parenthesis. In this case, length, does not have any trailing parenthesis.

More recently, Java now provides further syntax to loop through an array. Take a look at Listing 7. This syntax is similar to many of the language's foreach loop control.

public class ArrayLists {
   public static void main(String[] args) {
      String days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
      for (String i: days) {
         System.out.println(i);
      }
   }
}

Listing 7

An Array object contains various items besides the actual contents of the array. You already have discussed the length attribute. An Array object also inherits all the methods from the Object class. These methods are the trait that truly distinguishes a class from a simple variable—the fact that objects have behaviors implemented by methods.

According to the Sun documentation, the members of an array type are all of the following:

  • The public final field length, which contains the number of components of the array (length may be positive or zero)
  • The public method clone( ), which overrides the method of the same name in class Object and throws no checked exceptions
  • All the members inherited from class Object; the only method of Object that is not inherited is its clone method
  • Every array implements the interfaces Cloneable and java.io.Serializable

Interestingly, the Sun documentation provides an example of a class that mimics what an Array object would contain as seen in Listing 8.

class A implements Cloneable, java.io.Serializable {

   public final int length = X;
      public Object clone() {
      try {
         return super.clone();
      } catch (CloneNotSupportedException e) {
         throw new InternalError(e.getMessage());
      }
   }
}

Listing 8

The clone( ) method will take the contents of one array and copy them into a second one. A code example is seen in Listing 9. The point of interest is the following line.

String days2[] = (String[]) days1.clone();

Note the syntax for the casting and the use of the clone( ) method.

public class ArrayLists {

   public static void main(String[] args) {

      String days1[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
      String days2[] = (String[]) days1.clone();

      for (String i: days2) {
         System.out.println(i);
      }

   }
}

Listing 9

When the code for Listing 9 is executed, you get the same output as you would have by listing the contents of the array days1[].

C:column24>"C:Program FilesJavajdk1.5.0_06binjava" ArrayLists
Sun
Mon
Tue
Wed
Thu
Fri
Sat
C:column24>

Conclusion

Arrays were part of the original Java specification. When Java 2 was released, the Java collections were introduced. The use of Arrays may be a bit dated; however, they are a great starting point in the discussion of collections. When designing Java applications, collections other than Arrays may well be the best choice, yet using Arrays to begin the exploration of how objects relate to collections is instructive.

While Arrays are technically objects, there are three limitations that prevent Arrays from being like other objects.

  • You cannot inherit from it
  • As you have seen, the syntax is a bit different (in other words, directly accessing attributes)
  • You cannot customize an array (in other words, create your own methods).

For example, consider the code in Listing 10.

public class ArrayLists extends Array {
   public static void main(String[] args) {
   }
}

Listing 10

When this code is compiled, the following output is generated.

C:column24>"C:Program FilesJavajdk1.5.0_06binjavac" ArrayLists.java
ArrayLists.java:1: cannot find symbol
symbol: class Array
public class ArrayLists extends Array {
                    ^
1 error
C:column24>

The compiler will not let the Array class be extended.

In next month's column, you will continue your exploration of how objects relate to various collections.

References

  • www.sun.com
  • Just Java 2, 6th Edition. Peter van der Linden. 2004, Sun Microsystems.

About the Author

Matt Weisfeld is a faculty member at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a member of the Information Technology department, teaching programming languages such as C++, Java, and C# .NET as well as various web technologies. Prior to joining Tri-C, Matt spent 20 years in the information technology industry gaining experience in software development, project management, business development, corporate training, and part-time teaching. Matt holds an MS in computer science and an MBA in project management. Besides The Object-Oriented Thought Process
, which is now in it's second edition, Matt has published two other computer books, and more than a dozen articles in magazines and journals such as Dr. Dobb's Journal, The C/C++ Users Journal, Software Development Magazine, Java Report, and the international journal Project Management. Matt has presented at conferences throughout the United States and Canada.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date