Collections and Inner Classes


(来源:http://www.artima.com)

Objects and Java Seminar by Bill Venners
Lecture Handout

Agenda

  • Talk about collections
  • Differentiate nested and top-level classes
  • Look at nested top-level classes
  • Look at member inner classes
  • Look at local inner classes
  • Look at anonymous inner classes

Collections

  • Variable object counts: arrays and 1.2 Collections
  • Base interface: Collection
    • add and remove elements
    • query for size(), isEmpty(), and contains()
    • get an Iterator
  • List: ordered Collection
  • Set: Collection with no duplicates
  • Map: maintains key/value pairs (not a Collection)
  • When you get objects back, reference type is Object
  • Most implementations are unsynchronized

Maps

  • Map: maintains key/value pairs
  • Can look up a value with a key
  • ArrayMap: ArrayList implementation of Map
    • Fast creation/iteration for small maps
    • Slow for large maps
  • HashMap: hash table implementation of Map
    • For general use
    • Can tweak performance variables
  • TreeMap: red-black tree implementation of Map
    • Sorted in natural ordering
    • Elements implement Comparable (has an int compareTo(Object); method)
  • Hashtable: retrofitted - use HashMap

Lists

  • List: a Collection of ordered elements
    • get/set element at index
    • insert/delete element at index
    • get a ListIterator (traverse forward/backward, insert/remove in middle)
    • get a sublist
  • ArrayList: resizable array implementation of List
    • Fast random access
    • Slow insertion and deletion
    • Use ListIterator just for traversal, not insertion/deletion
  • LinkedList: linked list implementation of List
    • Slow random access
    • Fast insertion and deletion
    • Has add/get/remove of First() and Last() elements (stacks, queues, dequeues)
  • Vector: retrofitted - use ArrayList

Sets

  • Set: a Collection with no duplicate elements
    • Equality based on equals()
    • Same interface as Collection
    • Elements are unordered
  • ArraySet: resizable array implementation of Set
    • Use for small sets, created/destroyed often
    • Fast creation and iteration for small sets
    • Slow for large sets
  • HashSet: a HashMap implementation of Set
    • For general use
  • TreeSet: a TreeMap implementation of SortedSet
    • Sorted in natural ordering

Inner Classes and Top-Level Classes

  • Starting with 1.1, you can define classes as:
    • members of other classes
    • locally within a block of statements
    • anonymously within an expression
  • Code of inner classes and nested top-level classes can use simple names from enclosing scopes, including private members.
  • Classes that are members of packages are top-level classes.
  • Nesting top-level classes enables you to use a class to do package-like organization.
  • Inner classes have a reference to an enclosing instance.
  • A common use for inner classes is to define event handler "call backs."

Nested Top-Level Classes

  • Here's an example of a nested top-level class:
 1 // In file 
 2 // packagesaccess/examples/ex1/com/artima/somelib/MyLib.java
 3 package com.artima.somelib;
 4 
 5 public class MyLib {
 6 
 7     public static class SplitNameReturnVal {
 8 
 9         private String title;
10         private String first;
11         private String middle;
12         private String last;
13 
14         private SplitNameReturnVal(String title, String first, String
15 middle,
16             String last) {
17 
18             this.title = title;
19             this.first = first;
20             this.middle = middle;
21             this.last = last;
22         }
23 
24         public String getTitle() {
25             return title;
26         }
27 
28         public String getFirst() {
29             return first;
30         }
31 
32         public String getMiddle() {
33             return middle;
34         }
35 
36         public String getLast() {
37             return last;
38         }
39     }
40 
41     public static SplitNameReturnVal splitName(String name) {
42 
43         // Do processing to actually split the name into its
44         // component parts. For now, just return Jane Q. Public.
45         return new SplitNameReturnVal("Ms.", "Jane", "Q.", "Public");
46     }
47 }

 1 // In file
 2 // packagesaccess/examples/ex1/Test.java
 3 import com.artima.somelib.MyLib;
 4 
 5 class Test {
 6 
 7 
 8     public static void main(String[] args) {
 9 
10         MyLib.SplitNameReturnVal sn =
11             MyLib.splitName("Mr. F. Scott Fitzgerald");
12 
13         System.out.println(sn.getLast() + sn.getMiddle() + sn.getFirst()
14             + sn.getTitle());
15     }
16 }

 1 // In file
 2 // packagesaccess/examples/ex1/Test2.java
 3 import com.artima.somelib.MyLib;
 4 import com.artima.somelib.MyLib.SplitNameReturnVal;
 5 
 6 class Test2 {
 7 
 8 
 9     public static void main(String[] args) {
10 
11         SplitNameReturnVal sn =
12             MyLib.splitName("Mr. F. Scott Fitzgerald");
13 
14         System.out.println(sn.getLast() + sn.getMiddle() + sn.getFirst()
15             + sn.getTitle());
16     }
17 }

 1 // In file
 2 // packagesaccess/examples/ex1/Test3.java
 3 import com.artima.somelib.*;
 4 import com.artima.somelib.MyLib.*;
 5 
 6 class Test3 {
 7 
 8 
 9     public static void main(String[] args) {
10 
11         SplitNameReturnVal sn =
12             MyLib.splitName("Mr. F. Scott Fitzgerald");
13 
14         System.out.println(sn.getLast() + sn.getMiddle() + sn.getFirst()
15             + sn.getTitle());
16     }
17 }
  • Can use access specifiers on the nested top-level classes, just like any other class member.
  • Fully qualified name of a nested top-level class includes names of all enclosing classes separated by dots:
com.artima.somelib.MyLib.SplitNameReturnVal
  • Enclosing class's static members (including private members) are in scope in the nested class.
  • Compiler generates a class file for every class in MyLib.java:
$ javac MyLib.java
$ ls *.class
MyLib.class MyLib$SplitNameReturnVal.class

Inner Classes

  • Three kinds of inner classes:
    • member inner classes
    • local inner classes
    • anonymous inner classes
  • Inner classes contain a reference to their enclosing instance.
  • Simple names of enclosing classes are in scope, including private members.

Example: Top-Level Classes

  • Here's a CoffeeCup that serves as a generic "write-only" collection and returns an iterator:
 1 // In file CoffeeCup.java
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 
 5 public class CoffeeCup {
 6 
 7     private ArrayList innerObjects = new ArrayList();
 8 
 9     public void add(Object o) {
10         innerObjects.add(o);
11     }
12 
13     public Iterator getIterator() {
14 
15         return new CoffeeCupIterator(this);
16     }
17 
18     ArrayList getObjects() {
19 
20         return innerObjects;
21     }
22 
23     public String toString() {
24         return "CoffeeCup";
25     }
26 }
 1 // In file CoffeeCupIterator.java
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 
 5 class CoffeeCupIterator implements Iterator {
 6 
 7     private CoffeeCup myCup;
 8     private int next;
 9 
10     public CoffeeCupIterator(CoffeeCup myCup) {
11 
12         this.myCup = myCup;
13     }
14 
15     public boolean hasNext() {
16 
17         ArrayList al = myCup.getObjects();
18 
19         if (next < al.size()) {
20             return true;
21         }
22         else {
23             return false;
24         }
25     }
26 
27     public Object next() {
28 
29         ArrayList al = myCup.getObjects();
30 
31         Object retVal = al.get(next);
32         ++next;
33 
34         return retVal;
35     }
36 
37     public void remove() {
38         throw new UnsupportedOperationException();
39     }
40 }

1 
2 // In file examples/ex1/Coffee.java
3 public class Coffee {
4 
5     public String toString() {
6         return "Coffee";
7     }
8 }

1 
2 // In file examples/ex1/PaperClip.java
3 public class PaperClip {
4 
5     public String toString() {
6         return "PaperClip";
7     }
8 }

1 
2 // In file examples/ex1/Pen.java
3 public class Pen {
4 
5     public String toString() {
6         return "Pen";
7     }
8 }

1 
2 // In file examples/ex1/Pencil.java
3 public class Pencil {
4 
5     public String toString() {
6         return "Pencil";
7     }
8 }

 1 // In file Example1.java
 2 import java.util.Iterator;
 3 
 4 public class Example1 {
 5 
 6     public static void main(String[] args) {
 7 
 8         CoffeeCup cup = new CoffeeCup();
 9 
10         cup.add(new Pencil());
11         cup.add(new Pen());
12         cup.add(new PaperClip());
13         cup.add(new PaperClip());
14         cup.add(new Coffee());
15 
16         Iterator it = cup.getIterator();
17 
18         while (it.hasNext()) {
19 
20             Object o = it.next();
21             System.out.println(o.toString());
22         }
23     }
24 }
  • Output of Example1:
    Pencil
    Pen
    PaperClip
    PaperClip
    Coffee
    

Example: Member Inner Classes

  • Here's a version of the same CoffeeCup that uses a member inner class to define its iterator:
 1 // In file CoffeeCup.java
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 
 5 public class CoffeeCup {
 6 
 7     private ArrayList innerObjects = new ArrayList();
 8 
 9     public void add(Object o) {
10         innerObjects.add(o);
11     }
12 
13     private class CoffeeCupIterator implements Iterator {
14 
15         private int next;
16 
17         public boolean hasNext() {
18 
19             if (next < innerObjects.size()) {
20                 return true;
21             }
22             else {
23                 return false;
24             }
25         }
26 
27         public Object next() {
28 
29             Object retVal = innerObjects.get(next);
30             ++next;
31 
32             return retVal;
33         }
34 
35         public void remove() {
36             throw new UnsupportedOperationException();
37         }
38     }
39 
40     public Iterator getIterator() {
41 
42         return new CoffeeCupIterator();
43     }
44 
45     public String toString() {
46         return "CoffeeCup";
47     }
48 }
1 
2 // In file Coffee.java
3 public class Coffee {
4 
5     public String toString() {
6         return "Coffee";
7     }
8 }

1 
2 // In file PaperClip.java
3 public class PaperClip {
4 
5     public String toString() {
6         return "PaperClip";
7     }
8 }

1 
2 // In file Pen.java
3 public class Pen {
4 
5     public String toString() {
6         return "Pen";
7     }
8 }

1 
2 // In file Pencil.java
3 public class Pencil {
4 
5     public String toString() {
6         return "Pencil";
7     }
8 }

 1 // In file Example2.java
 2 import java.util.Iterator;
 3 
 4 public class Example2 {
 5 
 6     public static void main(String[] args) {
 7 
 8         CoffeeCup cup = new CoffeeCup();
 9 
10         cup.add(new Pencil());
11         cup.add(new Pen());
12         cup.add(new PaperClip());
13         cup.add(new PaperClip());
14         cup.add(new Coffee());
15 
16         Iterator it = cup.getIterator();
17 
18         while (it.hasNext()) {
19 
20             Object o = it.next();
21             System.out.println(o.toString());
22         }
23     }
24 }
25 
  • Output of Example2 is same as before:
    Pencil
    Pen
    PaperClip
    PaperClip
    Coffee
    
  • When you compile CoffeeCup.java, you get:
    CoffeeCup.class
    CoffeeCup$CoffeeCupIterator.class
    

Example: Local Inner Classes

  • Here's a version of the same CoffeeCup that uses a local inner class to define its iterator:
 1 // In file innerclasses/ex3/CoffeeCup.java
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 
 5 public class CoffeeCup {
 6 
 7     private ArrayList innerObjects = new ArrayList();
 8 
 9     public void add(Object o) {
10         innerObjects.add(o);
11     }
12 
13     public Iterator getIterator() {
14 
15         final String text = "Local CoffeeCupIterator";
16 
17         class CoffeeCupIterator implements Iterator {
18 
19             private int next;
20 
21             public boolean hasNext() {
22 
23                 if (next < innerObjects.size()) {
24                     return true;
25                 }
26                 else {
27                     return false;
28                 }
29             }
30 
31             public Object next() {
32 
33                 Object retVal = innerObjects.get(next);
34                 ++next;
35 
36                 return retVal;
37             }
38 
39             public void remove() {
40                 throw new UnsupportedOperationException();
41             }
42 
43             public String toString() {
44 
45                 return text;
46             }
47         }
48 
49         return new CoffeeCupIterator();
50     }
51 
52     public String toString() {
53         return "CoffeeCup";
54     }
55 }
1 
2 // In file innerclasses/ex3/Coffee.java
3 public class Coffee {
4 
5     public String toString() {
6         return "Coffee";
7     }
8 }

1 
2 // In file innerclasses/ex3/PaperClip.java
3 public class PaperClip {
4 
5     public String toString() {
6         return "PaperClip";
7     }
8 }

1 
2 // In file innerclasses/ex3/Pen.java
3 public class Pen {
4 
5     public String toString() {
6         return "Pen";
7     }
8 }

1 
2 // In file innerclasses/ex3/Pencil.java
3 public class Pencil {
4 
5     public String toString() {
6         return "Pencil";
7     }
8 }

 1 // In file innerclasses/ex3/Example3.java
 2 import java.util.Iterator;
 3 
 4 public class Example3 {
 5 
 6     public static void main(String[] args) {
 7 
 8         CoffeeCup cup = new CoffeeCup();
 9 
10         cup.add(new Pencil());
11         cup.add(new Pen());
12         cup.add(new PaperClip());
13         cup.add(new PaperClip());
14         cup.add(new Coffee());
15 
16         Iterator it = cup.getIterator();
17 
18         while (it.hasNext()) {
19 
20             Object o = it.next();
21             System.out.println(o.toString());
22         }
23 
24         System.out.println();
25         System.out.println(it.toString());
26     }
27 }
  • Output of Example3:
    Pencil
    Pen
    PaperClip
    PaperClip
    Coffee
    
    Local CoffeeCupIterator
    
  • When you compile CoffeeCup.java, you get:
    CoffeeCup.class
    CoffeeCup$1$CoffeeCupIterator.class
    

Example: Anonymous Inner Classes

  • Here's a version of the same CoffeeCup that uses an anonymous inner class to define its iterator:
 1 // In file innerclasses/ex4/CoffeeCup.java
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 
 5 public class CoffeeCup {
 6 
 7     private ArrayList innerObjects = new ArrayList();
 8 
 9     public void add(Object o) {
10         innerObjects.add(o);
11     }
12 
13     public Iterator getIterator() {
14 
15         final String text = "Anonymous Iterator";
16 
17         return new Iterator() {
18 
19             private int next;
20 
21             public boolean hasNext() {
22 
23                 if (next < innerObjects.size()) {
24                     return true;
25                 }
26                 else {
27                     return false;
28                 }
29             }
30 
31             public Object next() {
32 
33                 Object retVal = innerObjects.get(next);
34                 ++next;
35 
36                 return retVal;
37             }
38 
39             public void remove() {
40                 throw new UnsupportedOperationException();
41             }
42 
43             public String toString() {
44 
45                 return text;
46             }
47         };
48     }
49 
50     public String toString() {
51         return "CoffeeCup";
52     }
53 }
1 
2 // In file innerclasses/ex4/Coffee.java
3 public class Coffee {
4 
5     public String toString() {
6         return "Coffee";
7     }
8 }

1 
2 // In file innerclasses/ex4/PaperClip.java
3 public class PaperClip {
4 
5     public String toString() {
6         return "PaperClip";
7     }
8 }

1 
2 // In file innerclasses/ex4/Pencil.java
3 public class Pencil {
4 
5     public String toString() {
6         return "Pencil";
7     }
8 }

 1 // In file innerclasses/ex4/Example4.java
 2 import java.util.Iterator;
 3 
 4 public class Example4 {
 5 
 6     public static void main(String[] args) {
 7 
 8         CoffeeCup cup = new CoffeeCup();
 9 
10         cup.add(new Pencil());
11         cup.add(new Pen());
12         cup.add(new PaperClip());
13         cup.add(new PaperClip());
14         cup.add(new Coffee());
15 
16         Iterator it = cup.getIterator();
17 
18         while (it.hasNext()) {
19 
20             Object o = it.next();
21             System.out.println(o.toString());
22         }
23 
24         System.out.println();
25         System.out.println(it.toString());
26     }
27 }
  • Output of Example4:
    Pencil
    Pen
    PaperClip
    PaperClip
    Coffee
    
    Anonymous Iterator
    
  • When you compile CoffeeCup.java, you get:
    CoffeeCup.class
    CoffeeCup$1.class
    

Example: clone(), equals(), and hashcode()

  1 package com.artima.examples.matrix.ex2;
  2 
  3 import java.io.Serializable;
  4 
  5 /**
  6 * A two-dimensional matrix of <CODE>int</CODE>s.
  7 *
  8 * <P>
  9 * The <em>order</em> of
 10 * the matrix is its number of rows and columns. For example, the order
 11 * of a matrix with 5 rows and 4 columns is "5 by 4." A matrix with the
 12 * same number of rows and columns, such as a 3 by 3 matrix, is a
 13 * <em>square matrix</em>. A matrix all of whose elements is zero is
 14 * a <em>zero matrix</em>.
 15 *
 16 * <P>
 17 * Instances of <CODE>Matrix</CODE> are immutable.
 18 */
 19 public class Matrix implements Serializable, Cloneable {
 20 
 21     private int[][] elements;
 22     private int rowCount;
 23     private int colCount;
 24 
 25     /**
 26     * Construct a new square <code>Matrix</code> whose order is determined
 27     * by the passed number of rows.
 28     * Yields a zero matrix, i.e., all elements of the new <CODE>Matrix</CODE>
 29     * will be initialized to zero.
 30     *
 31     * @param rows The number of rows and cols in the new square <CODE>Matrix</CODE>
 32     * @exception IllegalArgumentException if <code>rows</code> or <code>cols</code> is less than 1
 33     */
 34     public Matrix(int rows) {
 35        if (rows < 1) {
 36             throw new IllegalArgumentException();
 37         }
 38         elements = new int[rows][rows];
 39         rowCount = rows;
 40         colCount = rows;
 41     }
 42 
 43     /**
 44     * Construct a new <EM>zero matrix</EM> whose order is determined
 45     * by the passed number of rows and columns. The order is (rows by columns).
 46     * Yields a zero matrix, i.e., all elements of the new <CODE>Matrix</CODE>
 47     * will be initialized to zero.
 48     *
 49     * @param rows The number of rows in the new <CODE>Matrix</CODE>
 50     * @param cols The number of columns in the new <CODE>Matrix</CODE>
 51     * @exception IllegalArgumentException if <code>rows</code> or <code>cols</code> is less than 1
 52     */
 53     public Matrix(int rows, int cols) {
 54         if (rows < 1 || cols < 1) {
 55             throw new IllegalArgumentException();
 56         }
 57         elements = new int[rows][cols];
 58         rowCount = rows;
 59         colCount = cols;
 60     }
 61 
 62     /**
 63     * Construct a new <CODE>Matrix</CODE> whose elements will be initialized
 64     * with values from the passed two-dimensional array of <CODE>int</CODE>s.
 65     * The order of the matrix will be determined by the sizes of the passed arrays.
 66     * For example, a two dimensional array constructed with <CODE>new int[4][9]</CODE>,
 67     * would yield a matrix whose order is 4 by 9. The lengths of each of the arrays
 68     * held from the initial array must be the same. The two-dimensional array passed
 69     * as <CODE>init</CODE> will not be used as part of the state of the newly constructed
 70     * <CODE>Matrix</CODE> object.
 71     *
 72     * @param rows The number of rows in the new <CODE>Matrix</CODE>
 73     * @param cols The number of columns in the new <CODE>Matrix</CODE>
 74     * @exception IllegalArgumentException if the length of any passed array is zero,
 75     *    or if the length of all the secondary arrays are not equivalent.
 76     */
 77     public Matrix(int[][] init) {
 78 
 79         checkValidity(init);
 80 
 81         elements = (int[][]) init.clone();
 82         rowCount = init.length;
 83         colCount = init[0].length;
 84     }
 85 
 86     /**
 87     * Returns the element value at the specified row and column.
 88     *
 89     * @param row The row of the element whose value is to be returned
 90     * @param col The column of the element whose value is to be returned
 91     * @return value of element at specified row and column
 92     * @exception IndexOutOfBoundsException if <code>row</code> is
 93     *    less than zero or greater than the number of rows minus 1, or if
 94     *    <code>col</code> is less than 0 or greater than the number of
 95     *    columns minus 1.
 96     */
 97     public int get(int row, int col) {
 98         checkIndices(row, col);
 99         return elements[row][col];
100     }
101 
102     /**
103     * Returns the number of rows in this <code>Matrix</code>.
104     *
105     * @return number of rows in this <code>Matrix</code>
106     */
107     public int getRows() {
108         return rowCount;
109     }
110 
111     /**
112     * Returns the number of columns in this <code>Matrix</code>.
113     *
114     * @return number of columns in this <code>Matrix</code>
115     */
116     public int getCols() {
117         return colCount;
118     }
119 
120     /**
121     * Adds the passed <code>Matrix</code> to this one.
122     * The order of the passed <code>Matrix</code> must be identical
123     * to the order of this <code>Matrix</code>.
124     *
125     * <P>
126     * The sum of two <code>Matrix</code> objects is a <code>Matrix</code>
127     * of the same order of the two addends. Each element of the sum
128     * <code>Matrix</code> is equal to the sum of the corresponding elements
129     * in the <code>Matrix</code> addends. For example:
130     *
131     * <PRE>
132     * | 1 2 3 |   |  9 -8  7 |   | 10 -6 10 |
133     * | 4 5 6 | + | -6  5 -4 | = | -2 10  2 |
134     * | 7 8 9 |   | -3  2 -1 |   |  4 10  8 |
135     * </PRE>
136     *
137     * <P>
138     * This method does not throw any exception on overflow.
139     *
140     * @param addend the <code>Matrix</code> to add to this one
141     * @return The sum of this <code>Matrix</code> and the passed <code>Matrix</code>
142     * @exception IllegalArgumentException if the order of the passed
143     *     <code>Matrix</code> object differs from the order of this <code>Matrix</code>
144     */
145     public Matrix add(Matrix addend) {
146 
147         // Make sure addend has the same order as this matrix
148         if ((addend.rowCount != rowCount) || (addend.colCount != colCount)) {
149             throw new IllegalArgumentException();
150         }
151 
152         Matrix retVal = new Matrix(elements);
153         for (int row = 0; row < rowCount; ++row) {
154             for (int col = 0; col < colCount; ++col) {
155                 retVal.elements[row][col] += addend.elements[row][col];
156             }
157         }
158         return retVal;
159     }
160 
161     /**
162     * Subtracts the passed <code>Matrix</code> from this one.
163     * The order of the passed <code>Matrix</code> must be identical
164     * to the order of this <code>Matrix</code>. Returned <code>Matrix</code>
165     * equals the sum of this <code>Matrix</code> and the negation of the
166     * passed <code>Matrix</code>.
167     *
168     * <P>
169     * The difference of two <code>Matrix</code> objects is a <code>Matrix</code>
170     * of the same order of the minuend and subtrahend. Each element of the sum
171     * <code>Matrix</code> is equal to the difference of the corresponding elements
172     * in the minuend (this) and subtrahend (passed) <code>Matrix</code> objects.
173     * For example:
174     *
175     * <PRE>
176     * | 1 2 3 |   |  9 -8  7 |   | -8 10 -4 |
177     * | 4 5 6 | - | -6  5 -4 | = | 10  0 10 |
178     * | 7 8 9 |   | -3  2 -1 |   | 10  6 10 |
179     * </PRE>
180     *
181     * <P>
182     * This method does not throw any exception on overflow.
183     *
184     * @param subtrahend the <code>Matrix</code> to subtract from this one
185     * @return The difference of this <code>Matrix</code> and the passed <code>Matrix</code>
186     * @exception IllegalArgumentException if the order of the passed
187     *     <code>Matrix</code> object differs from the order of this <code>Matrix</code>
188     */
189     public Matrix sub(Matrix subtrahend) {
190 
191         // To be subtracted, subtrahend must have the same order
192         if ((subtrahend.rowCount != rowCount) || (subtrahend.colCount != colCount)) {
193             throw new IllegalArgumentException();
194         }
195 
196         Matrix retVal = new Matrix(elements);
197         for (int row = 0; row < rowCount; ++row) {
198             for (int col = 0; col < colCount; ++col) {
199                 retVal.elements[row][col] -= subtrahend.elements[row][col];
200             }
201         }
202         return retVal;
203     }
204 
205     /**
206     * Multiplies this matrix by the passed scalar. Returns
207     * a new matrix representing the result of the multiplication.
208     * To negate a matrix, for example, just multiply it by
209     * -1.
210     *
211     * <P>
212     * The product of a <code>Matrix</code> and a scalar is a <code>Matrix</code>
213     * of the same order as the <code>Matrix</code> multiplicand. Each element of the product
214     * <code>Matrix</code> is equal to the product of the corresponding element
215     * in the <code>Matrix</code> multiplicand and the scalar multiplier. For example:
216     *
217     * <PRE>
218     *      | 1 2 3 |   |  -2  -4  -6 |
219     * -2 * | 4 5 6 | = |  -8 -10 -12 |
220     *      | 7 8 9 |   | -14 -16 -18 |
221     * </PRE>
222     *
223     * <P>
224     * This method does not throw any exception on overflow.
225     *
226     * @param addend the <code>Matrix</code> to add to this one
227     * @return The sum of this <code>Matrix</code> and the passed <code>Matrix</code>
228     * @exception IllegalArgumentException if the order of the passed
229     *     <code>Matrix</code> object differs from the order of this <code>Matrix</code>
230     */
231     public Matrix mult(int scalar) {
232 
233         Matrix retVal = new Matrix(elements);
234         for (int row = 0; row < rowCount; ++row) {
235             for (int col = 0; col < colCount; ++col) {
236                 retVal.elements[row][col] *= scalar;
237             }
238         }
239         return retVal;
240     }
241 
242     /**
243     * Multiplies this <code>Matrix</code> (the multiplicand) by the passed
244     * <code>Matrix</code> (the multiplier). The number of columns in this
245     * multiplicand <code>Matrix</code> must equal the number rows in the
246     * passed multiplier <code>Matrix</code>.
247     *
248     * <P>
249     * The product of two <code>Matrix</code> objects is a <code>Matrix</code> that has
250     * the same number of rows as the multiplicand (this <code>Matrix</code>) and the
251     * same number of columns as the multiplier (passed <code>Matrix</code>).
252     * Each element of the product <code>Matrix</code> is equal to sum of the products
253     * of the elements of corresponding multiplicand row and multiplier column.
254     * For example:
255     *
256     * <PRE>
257     * | 0 1 |   | 6 7 |   | (0*6 + 1*8) (0*7 + 1*9) |   |  8  9 |
258     * | 2 3 | * | 8 9 | = | (2*6 + 3*8) (2*7 + 3*9) | = | 36 41 |
259     * | 4 5 |             | (4*6 + 5*8) (4*7 + 5*9) |   | 64 73 |
260     * </PRE>
261     *
262     * <P>
263     * This method does not throw any exception on overflow.
264     *
265     * @param multiplier the <code>Matrix</code> to multiply to this one
266     * @return A new <code>Matrix</code> representing the product of this
267     *    <code>Matrix</code> and the passed <code>Matrix</code>
268     * @exception IllegalArgumentException if the number of rows of the passed
269     *     <code>Matrix</code> object differs from the number of columns of
270     *     this <code>Matrix</code>
271     */
272     public Matrix mult(Matrix multiplier) {
273 
274         // To do a matrix multiplication, the number of columns in this
275         // matrix must equal the number of rows of the passed multiplicand.
276         if (colCount != multiplier.rowCount) {
277             throw new IllegalArgumentException();
278         }
279 
280         // Calculate order of result
281         int resultRows = rowCount;
282         int resultCols = multiplier.colCount;
283 
284         // Create array for result
285         int[][] resultArray = new int[resultRows][resultCols];
286 
287         Matrix retVal = new Matrix(elements);
288         for (int row = 0; row < resultRows; ++row) {
289             for (int col = 0; col < resultCols; ++col) {
290                 for (int i = 0; i < colCount; ++i) {
291                     resultArray[row][col] += elements[row][i] * multiplier.elements[i][col];
292                 }
293             }
294         }
295         return retVal;
296     }
297 
298     /**
299     * Returns a <code>String</code> that contains the
300     * integer values of the elements of this
301     * <code>Matrix</code>. Each row of element values
302     * is enclosed in parentheses and separated by
303     * commas, and the entire result is enclosed in
304     * a set of parentheses. For example, for the matrix:
305     *
306     * <PRE>
307     * | 1 2 3 |
308     * | 4 5 6 |
309     * | 7 8 9 |
310     * </PRE>
311     *
312     * This method would return the string:
313     *
314     * <PRE>
315     * ((1, 2, 3), (4, 5, 6), (7, 8, 9))
316     * </PRE>
317     *
318     * @return A new <code>String</code> representation of the state of
319     *    this <code>Matrix</code>
320     */
321     public String toString() {
322 
323         StringBuffer retVal = new StringBuffer("(");
324 
325         for (int row = 0; row < rowCount; ++row) {
326             retVal.append("(");
327             for (int col = 0; col < colCount; ++col) {
328                 retVal.append(elements[row][col]);
329                 if (col != colCount - 1) {
330                     retVal.append(", ");
331                 }
332             }
333             retVal.append(")");
334             if (row != rowCount - 1) {
335                 retVal.append(", ");
336             }
337         }
338         retVal.append(")");
339         return retVal.toString();
340     }
341 
342     /**
343     * Clones this object. 
344     *
345     * @return A clone of this <code>Matrix</code>
346     */
347     public Object clone() {
348         try {
349             Matrix clone = (Matrix) super.clone();
350             clone.elements = new int[rowCount][colCount];
351 
352             for (int row = 0; row < rowCount; ++row) {
353                 for (int col = 0; col < colCount; ++col) {
354                     clone.elements[row][col] = elements[row][col];
355                 }
356             }
357             return clone;
358         }
359         catch (CloneNotSupportedException e) {
360             // Can't happen
361             throw new InternalError();
362         }
363     }
364 
365     /**
366     * Compares passed <CODE>Matrix</CODE> to this
367     * <code>Matrix</code> for equality. Two <code>Matrix</code>
368     * objects are semantically equal if they have the same
369     * order (i.e., same number of rows and columns), and
370     * the <code>int</code> value of each element in
371     * this <code>Matrix</code> is equal to the corresponding
372     * <code>int</code> value in the passed <code>Matrix</code>.
373     *
374     * @param An object to compare to this <code>Matrix</code>
375     * @return <code>true</code> if this <code>Matrix</code> is semantically equal
376     *    to the passed <code>Matrix</code>
377     */
378     public boolean equals(Object o) {
379 
380         if ((o == null) || (getClass() != o.getClass())) {
381             return false;
382         }
383 
384         Matrix m = (Matrix) o;
385 
386         // Because this class extends Object, don't
387         // call super.equals()
388 
389         // To be semantically equal, both matrices must
390         // have the same order
391         if ((rowCount != m.rowCount) || (colCount != m.colCount)) {
392             return false;
393         }
394 
395         // To be semantically equal, corresponding
396         // elements of both matrices must be equal
397         for (int row = 0; row < rowCount; ++row) {
398             for (int col = 0; col < colCount; ++col) {
399 
400                 if (elements[row][col] != m.elements[row][col]) {
401                     return false;
402                 }
403             }
404         }
405 
406         return true;
407     }
408 
409     /**
410     * Computes the hash code for this <code>Matrix</code>.
411     *
412     * @return a hashcode value for this <code>Matrix</code>
413     */
414     public int hashcode() {
415 
416         int retVal = rowCount * colCount;
417 
418         for (int row = 0; row < rowCount; ++row) {
419             for (int col = 0; col < colCount; ++col) {
420 
421                 retVal *= elements[row][col];
422             }
423         }
424 
425         return retVal;
426     }
427 
428     /**
429     * Ensures passed two-dimensional array is valid
430     * for initializing a <CODE>Matrix</CODE> object.
431     */ 
432     private static void checkValidity(int[][] val) {
433 
434         try {
435             int rows = val.length;
436             if (rows == 0) {
437                 throw new IllegalArgumentException();
438             }
439             int cols = val[0].length;
440             if (cols == 0) {
441                 throw new IllegalArgumentException();
442             }
443             for (int i = 1; i < rows; ++i) {
444                 if (val[i].length != cols) {
445                     throw new IllegalArgumentException();
446                 }
447             }
448         }
449         catch (NullPointerException e) {
450             throw new IllegalArgumentException();
451         }
452     }
453 
454     /**
455     * Ensures passed row and column represent valid indices into
456     * this <CODE>Matrix</CODE>.
457     */
458     private void checkIndices(int row, int col) {
459         if (row >= rowCount || row < 0 || col >= colCount || col < 0) {
460             throw new IndexOutOfBoundsException();
461         }
462     }
463 }

Exercises

Problem 1.

In the InnerClasses/ex2 directory, change the implementation of the collection in CoffeeCup from ArrayList to LinkedList. In the CoffeeCupIterator inner class, implement the remove() method so that it actually removes the element rather than throwing UnsupportedOperationException. Change the Example2 program so that it iterates through the collection twice. The first time you iterate, remove all PaperClips. The second time you iterate, just print out the return value of toString() invoked on all the objects in the collection.

Problem 2.

In the InnerClasses/ex3 directory, change the implementation of the collection in CoffeeCup from ArrayList to LinkedList. Then, add a getListIterator() method to the CoffeeCup class. This method should just get a ListIterator from the innerObjects List and return it. Finally, change Example3 so that it gets a ListIterator rather than an Iterator. With the ListIterator, iterate through the objects contained in the CoffeeCup twice, once forwards and once backwards. As you iterate in either direction, print out the return value of toString() invoked on all the objects in the collection.

Problem 3.

In the InnerClasses/ex3 directory, change Example3 so that the first time you iterate with the ListIterator (in the forward direction) you insert a each object back into the list a second time. The copy should follow the original in the list. (Continue printing out the return value of toString() invoked on all the originals the first time through. Run Example3 application. You should see the copies show up in the second iteration, which traverses the list backwards.

Problem 4.

In the InnerClasses/ex3 directory, change Example3 so that the first time you iterate with the ListIterator (in the forward direction) you insert a clone of each object back into the list. The clone should follow the original in the list. (Continue printing out the return value of toString() invoked on all the originals the first time through. Run Example3 application. You should see the clones show up in the second iteration, which traverses the list backwards.