Data Structures in Java: Part 16

Baldwin shows you how to use the more-complex version of the toArray method declared in the Collection interface.  He discusses issues regarding the type of the array and the types of the objects referred to by the elements in the collection.  He also discusses issues regarding the relative sizes of the array and the collection.  Finally, he reaffirms that you need to exercise caution when using the elements stored in the array, to avoid corrupting the state of the objects referred to by the elements in the collection.

The toArray Method, Part 2

Published:  September 24, 2001
By Richard G. Baldwin

Java Programming, Lecture Notes # 1380


Preface

A miniseries

This lesson is part of a miniseries on Java data structures and the Java Collections Framework.  The first lesson in the miniseries was entitled Data Structures in Java: Part 1, Getting Started.  The previous lesson was entitled Data Structures in Java: Part 15, The toArray Method, Part 1.

The purpose of this miniseries is to help you learn the essential features of Object-Oriented data structures in Java using the Collections Framework.

A sub-series

This is also the second (and last) lesson in a sub-series on the toArray method.  The primary purpose of the lessons in this sub-series is to teach you how to use the overloaded versions of the toArray method, which are declared in the Collection interface.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at Baldwin's Java Programming Tutorials.

Preview

The Collection interface declares the following two overloaded versions of the toArray method:

public Object[] toArray()
public Object[] toArray(Object[] a)

In the previous lesson, I taught you how to use the first (simpler) of the two methods.  I also discussed the need to exercise care when using the elements stored in the returned array to avoid corrupting the state of the objects referred to by elements in the collection.

In this lesson, I will teach you how to use the second (more-complex) version of the toArray method declared in the Collection interface.  I will discuss issues regarding the type of the array and the types of the objects referred to by the elements in the collection.  I will also discuss issues regarding the relative sizes of the array and the collection.

Finally, I will reaffirm that you need to exercise care when using the elements stored in the array, to avoid corrupting the state of the objects referred to by the elements in the collection.

Discussion and Sample Program

Beginning with a quiz

As has been the case in the last few lessons, let's begin with a little quiz to test your prior knowledge of the Collections Framework.  To take this quiz, examine the program shown in Listing 1 and write down the output produced by that program.
 
//File ToArray02.java
//Copyright 2001, R.G.Baldwin
import java.util.*;
import javax.swing.*;

public class ToArray02{
  public static void main(
                        String args[]){
    new Worker().doIt();
  }//end main()
}//end class ToArray02
//===================================//

class Worker{
  public void doIt(){
    Collection ref;
    
    //Create, populate, and display
    // the contents of an array
    JComponent[] array = 
                     new JComponent[8];
    for(int cnt=0;cnt<8;cnt++){
      array[cnt] = new JButton();
      array[cnt].setToolTipText(
                        "" + (cnt+10));
    }//end for loop
    System.out.println();
    showArray(array,
            "Original array contents");


    //Create, populate, and display the
    // contents of a collection
    ref = new LinkedList();
    Populator.fillIt(ref);
    showCollection(ref,
                "Collection contents");
    
    //Get collection contents into the
    // array and display the new
    // contents of the array.
    array = (JComponent[])ref.
                        toArray(array);
    showArray(array,
                 "New array contents");

    //Modify a property of an object
    // referred to by one of the
    // elements in the array. Display
    // array contents after 
    // modification
    ((JComponent)array[0]).
                  setToolTipText("XX");
    showArray(array,
            "Modified array contents");
    
    //Display the contents of the
    // collection
    showCollection(ref,
                "Collection contents");
  }//end doIt()
//-----------------------------------//
    
  //Utility method for displaying
  // array contents
  void showArray(Object[] array,
                         String title){
    System.out.println(title);
    for(int i = 0; i < array.length;
                                  i++){
      if(array[i] == null){
        System.out.print("null ");
      }else{
        System.out.print(
                ((JComponent)array[i]).
               getToolTipText() + " ");
      }//end else
    }//end for loop
    System.out.println();
  }//end showArray()
//-----------------------------------//

  //Utility method for displaying
  // collection contents
  void showCollection(Collection ref,
                         String title){
    System.out.println(title);
    Iterator iter = ref.iterator();
    while(iter.hasNext()){
      System.out.print(
             ((JComponent)iter.next()).
               getToolTipText() + " ");
    }//end while loop
    System.out.println();
  }//end showCollection
}// end class Worker
//===================================//

class Populator{
  public static void fillIt(
                       Collection ref){
    ref.add(new JButton());
    ref.add(new JButton());
    ref.add(new JLabel());
    ref.add(new JButton());
    ref.add(new JButton());
    ref.add(new JLabel());
    
    Iterator iter = ref.iterator();
    int cnt = 0;
    JComponent refVar;
    while(iter.hasNext()){
      refVar = (JComponent)iter.next();
      if(refVar instanceof JButton){
        refVar.setToolTipText(
                            "B"+cnt++);
      }else{
        refVar.setToolTipText(
                          "L" + cnt++);
      }//end else
    }//end while loop
    
  }//end fillIt()
}//end class Populator

Listing 1

And the answer is ...

The correct answer to the quiz is the program output shown below:

Original array contents
10 11 12 13 14 15 16 17
Collection contents
B0 B1 L2 B3 B4 L5
New array contents
B0 B1 L2 B3 B4 L5 null 17
Modified array contents
XX B1 L2 B3 B4 L5 null 17
Collection contents
XX B1 L2 B3 B4 L5

If that was your answer, you probably already understand most of the material covered in this lesson.  In that case, you might consider skipping this lesson and moving on to the next lesson.  If that wasn't your answer, you should probably continue with your study of this lesson.

Similar to previous program

Except for the use of a different version of the toArray method, the overall structure of the program in Listing 1 is similar to the program in the previous lesson.  Therefore, I will concentrate on those aspects of this program that differentiate it from the program in the previous lesson.

A populated array

Unlike the program in the previous lesson, the code in Listing 2 creates and populates an eight-element array of type JComponent.  This is the array that will be re-populated by the toArray method later in the program.  The array is populated with a set of initial element values at this point to make it obvious when it is re-populated (overwritten elements) by the toArray method later.
 
    JComponent[] array = 
                     new JComponent[8];

    for(int cnt=0;cnt<8;cnt++){
      array[cnt] = new JButton();
      array[cnt].setToolTipText(
                        "" + (cnt+10));
    }//end for loop
    System.out.println();
    showArray(array,
            "Original array contents");

Listing 2

The JButton class, the JLabel class, and the setToolTipText method were discussed in detail in the previous lesson, so I won't repeat that discussion here.

Display the array contents

After the array is populated by the code in Listing 2, a reference to the array object is passed to the showArray method (also in Listing 2) to display the contents of the array.

With the exception of some minor changes implemented in this program to make the use of the showArray method more compact, this is the same showArray method used in the previous lesson.  Therefore, I won't discuss that method further in this lesson.  The output produced by the code in Listing 2 is as follows:

Original array contents
10 11 12 13 14 15 16 17

As you can see, each of the eight elements in the array was initialized with an easily-recognizable and unique value, (which may be overwritten by the toArray method later).

A new LinkedList collection

The code in Listing 3 creates and populates a new LinkedList collection.  The collection is populated by passing the LinkedList object's reference to a method named fillIt.

The code in Listing 2 also displays the contents of the LinkedList collection after it has been populated.  The list is displayed by passing the LinkedList object's reference to a method named showCollection.
 
    ref = new LinkedList();
    Populator.fillIt(ref);
    showCollection(ref,
                "Collection contents");

Listing 3

Except for a couple of minor changes to the showCollection method, the code to create, populate, and display the collection is the same as the code in the previous lesson.

Populating the LinkedList collection

A couple of points regarding the fillIt method (shown in Listing 4) are worthy of note.
 
  public static void fillIt(
                       Collection ref){
    ref.add(new JButton());
    ref.add(new JButton());
    ref.add(new JLabel());
    ref.add(new JButton());
    ref.add(new JButton());
    ref.add(new JLabel());
    
    Iterator iter = ref.iterator();
    int cnt = 0;
    JComponent refVar;
    while(iter.hasNext()){
      refVar = (JComponent)iter.next();
      if(refVar instanceof JButton){
        refVar.setToolTipText(
                            "B"+cnt++);
      }else{
        refVar.setToolTipText(
                          "L" + cnt++);
      }//end else
    }//end while loop
    
  }//end fillIt()

Listing 4

The fillIt method begins by invoking the add method six times in succession, passing references to new anonymous objects (of types JButton and JLabel) as a parameter to the add method.

Four buttons and two labels

Four of the objects are instantiated from the class named JButton.  The other two objects are instantiated from the class named JLabel.

Both JButton and JLabel belong to the javax.swing package.  Further, both are subclasses of the class named JComponent.

The toolTipText property

Finally, both classes have a property named toolTipText, which can be set and accessed by invoking the following methods on a reference to the object:

void setToolTipText(String text)
String getToolTipText()

JButton and JLabel

I chose to use objects of these two classes for illustration purposes simply because they possess the characteristics that I need for this lesson.  Those characteristics are:

Making the objects distinguishable

After adding the objects' references to the collection, the code in Listing 4 uses the setToolTipText method to store a unique String value in the toolTipText property of the object referred to by each of the elements in the collection.

Identifying the buttons and labels

In addition to storing a unique value in the toolTipText property of the object referred to by each element, the code in Listing 4 also makes it possible to distinguish between the JButton objects and the JLabel objects.  This is accomplished by including an upper-case "B" in the property value for each JButton, and including an upper-case "L" in the property value for each JLabel button.

Display the collection

The code in Listing 3 above invokes the showCollection method to display the contents of the populated LinkedList collection.  The output produced by the code in Listing 3 is shown below:

Collection contents
B0 B1 L2 B3 B4 L5

Each term in the output is the String value of the toolTipText property for a particular object.  Hence, there are six terms in the output, one for each element in the collection.

Copy collection elements into an array

We have now reached the point that is the main thrust of this lesson.

The code in Listing 5 shows how to use the more-complex version of the toArray method to copy the elements in the collection into an array.
 
    array = (JComponent[])ref.
                        toArray(array);
    showArray(array,
                 "New array contents");

Listing 5

The code in Listing 5 also causes the contents of the array to be displayed after it receives the elements from the collection.

The first statement in Listing 5 causes the first seven elements in the array to be overwritten with element values from the collection (plus one null value).

The second statement in Listing 5 causes the contents of the array to be displayed.

The toArray method

The most important thing to note about Listing 5 is that a reference to an array object is passed as a parameter to the toArray method. (The simpler version of the toArray method, discussed in the previous lesson, doesn't take any parameters.)

The essential difference

The essential difference between the two overloaded versions of the toArray method has to do with the origin of the array into which the toArray method copies the elements from the collection.

With the simpler version of the toArray method that takes no parameters, the toArray method creates a new array object of type Object, populates it, and returns that object's reference as type Object.

Type is not an issue for the simpler version

Since the new array object is of type Object, there are no issues regarding type compatibility between the type of the array and the types of the elements stored in the collection.  A reference to an object of any type can be stored in an array of the generic type Object[].

Size is not an issue for the simpler version

Also, since the array is created when it is needed by the simpler version of the toArray method, there are also no size issues.  The array is created to be of exactly the correct size to contain copies of all of the elements in the collection.

More-complex version presents some issues

With the more-complex version of the toArray method (shown in Listing 5), the programmer must provide the array object that will be populated by the toArray method.  In this situation, there are size issues as well as type issues to be dealt with.

The type issue

Here is some of what the Sun documentation for the LinkedList class has to say about the type issue for this version of the toArray method:

"Returns an array containing all of the elements in this list in the correct order. The runtime type of the returned array is that of the specified array. ...  Throws:  ArrayStoreException - if the runtime type of (the specified array) is not a supertype of the runtime type of every element in this list.
In other words, the type of the array passed as a parameter to the toArray method must be a superclass of the classes from which all of the objects being managed by the collection were instantiated.

Two types of objects in this collection

In this program, the collection is managing objects of the types JButton and JLabel.  Each of these types is a subclass of the class named JComponent.  For that reason, the type of array that I instantiated and passed to the toArray method is JComponent[].

The size issue

Here is some of what the Sun documentation for the LinkedList class has to say about the size issue for this version of the toArray method.

"If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.

If the list fits in the specified array with room to spare (i.e., the array has more elements than the list), the element in the array immediately following the end of the collection is set to null. This is useful in determining the length of the list only if the caller knows that the list does not contain any null elements."

So, what did I do?

Knowing all of this in advance, I purposely caused the size of the JComponent array to be larger (by two elements) than the number of elements in the collection.  Therefore, the array that I passed to the toArray method was populated and a reference to that populated array was returned.

(Had my array been smaller than the number of elements in the collection, the toArray method would have created and populated a new array of type JComponent and would have returned a reference to that new array object.  In that case, my array would have been used by the toArray method only for the purpose of determining the runtime type of my array.)"

More information from Sun

Here is some additional information about the toArray method provided by the Sun documentation for the Collection interface:

"If this collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order."
Because the iterator for a LinkedList object returns the elements in increasing index order, the toArray method, in this case, copies the element at each index position in the collection into the element at the same index position in the array.  Thus, reference values are copied from each element in the collection into the first six elements in the array.

The output

The output produced by the code in Listing 5 is shown below:

New array contents
B0 B1 L2 B3 B4 L5 null 17

You will note that the first six elements in the array match the six elements in the collection (the initial values placed in the array earlier when the array was instantiated have been overwritten).

You will also note that the value of the seventh element in the array (index value 6) has been overwritten with a null reference.

Demonstrates same array was used

Note finally that the last element in the array was not overwritten.  It still contains the value placed there when the array object was instantiated.  This demonstrates that the array that I passed to the toArray method was populated with the collection data, and a reference to that array was returned by the toArray method.

What if the array was too small?

Had my array been too small, it would have been discarded by the toArray method.  The toArray method would have created and populated a new array object of the correct size and runtime type, and would have returned a reference to that new array.

Not difficult to demonstrate

Although this is not demonstrated by this program, it is easy to modify the program to demonstrate this feature.

A String representation of the array object can be displayed using a System.out.println(array) statement before and after the array is passed to the toArray method.

Array as large as or larger than collection

For the cases where my array contained six, seven, or eight elements, and the collection contained six elements, the String representations of the array object before and after the invocation of the toArray method were the same.  For one case, those String representations were as follows:

[Ljavax.swing.JComponent;@49ba38
[Ljavax.swing.JComponent;@49ba38

In other words, the reference variable named array referred to the same array object before and after the invocation of the toArray method.

Array smaller than the collection

When I reduced the size of the array to five elements, keeping the size of the collection at six elements, the before and after String representations of the array object were as follows:

[Ljavax.swing.JComponent;@506411
[Ljavax.swing.JComponent;@21807c

In this case, the reference to the array object returned by the toArray method was different from the reference that was passed to the toArray method.  In other words, the returned reference referred to a different array object than was referred to by the reference that was passed to the toArray method.

Modify an object

The code shown in Listing 6 modifies the value of the toolTipText property of the object whose reference is stored in index 0 of the array.
 
    ((JComponent)array[0]).
                  setToolTipText("XX");
    showArray(array,
            "Modified array contents");
    
    showCollection(ref,
                "Collection contents");


Listing 6

The code in Listing 6 also displays the contents of the array and the contents of the collection after the modification is made.

The output produced by the code in Listing 6 is shown below:

Modified array contents
XX B1 L2 B3 B4 L5 null 17
Collection contents
XX B1 L2 B3 B4 L5

Now for the caution

Note that the value of the toolTipText property of the object referred to by the reference at index 0 of the array, and the same property of the object referred to by the reference at index 0 of the collection was overwritten by "XX".  (This is true because both references refer to the same object.)

This is the case regardless of which version of the toArray method is used.  Therefore, the same cautions discussed in the previous lesson apply here as well.

Summary

In this lesson, I taught you how to use the more-complex version of the two overloaded versions of the toArray method, declared in the Collection interface, to copy the elements from a collection into an array of type JComponent.

I discussed issues regarding the type of the array and the type of the objects referred to by the elements in the container.  I also discussed issues regarding the size of the array as compared to the number of elements in the collection.

Finally, I reaffirmed that you need to exercise care when using the elements stored in the array, to avoid corrupting the state of the objects referred to by the elements in the collection.

What's Next?

The next several lessons will teach you how to use the Map and SortedMap interfaces, and the concrete class implementations of those interfaces.


Copyright 2001, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin.richard@iname.com

-end-