Lecture 12

Objects and Equality

A common task in programming in an object-oriented language (like Java) is determining when two objects are "equal".  Generally, two objects are "equal" when they contain the same contents.  For example, quite often we need to check two String objects to see if they are equal, meaning that they represent the same sequence of characters.

When you want to compare two values of a primitive type (int, double, etc.) to see if the values are equal, you can use the equality (==) or inequality (!=) operators.  Unfortunately, these operators don't do what you might expect when you use them to compare two object references.  Instead, you should use the equals method.  The following program illustrates the problem:

public class StringEqualityDemo {
  public static String makeGreeting(String name) {
    String greeting;
    greeting = "Hello, " + name;
    return greeting;
  }
  
  public static void main(String[] args) {
    String g1 = makeGreeting("Alice");
    String g2 = makeGreeting("Alice");
    
    System.out.println("g1: " + g1);
    System.out.println("g2: " + g2);
    System.out.println("g1.equals(g2): " + g1.equals(g2));
    System.out.println("g1==g2: " + (g1 == g2));
  }
}

Here is the output of this program:

g1: Hello, Alice
g2: Hello, Alice
g1.equals(g2): true
g1==g2: false

The program uses the makeGreeting method to create two identical string values.  The equals method finds that the two strings are equal: however, the equality operator pronounces them to be unequal.  This is because when used to compare references to objects, the equality and inequality operators determine whether or not the references refer to the same object, not whether or not their contents are equal.  In the example above, the variables g1 and g2 refer to different strings objects, even though they both represent the same sequence of characters.  This leads to the following rule for comparing objects:

When comparing two objects to see whether they contain the same contents, use the equals method.

The equals method may be used on any object.  It takes the object to be compared to as a parameter, and returns a boolean result, true if the contents of the two objects are identical, false if not.

Arrays

An array is an object that stores a sequence of values.  Arrays have two important characteristics:

  1. Element type: what type of values it can store.  For example, int, char, double, String, etc.
  2. length: the number of elements it can store.

An array variable, like any other variable, is declared by specifying its type and then an identifier which names the variable.  An array type is written by first specifying the element type and then adding the symbols "[]" to specify that the type is an array.  For example:

// Declare a variable "temperatures" to refer to an array of double values
double[] temperatures;

"double[]" is the type "array of double".  double is the element type, so this array will store double values.  "temperatures" is the name of the array variable.

Like all reference variables, an array variable does not immediately refer to an array.  It must be initialized with a reference to an array before it can be used.  A new array is created using the new operator, in a manner that is quite similar to using the new operator to create an instance of a class:

// Create an array of doubles of length 7
temperatures = new double[7];

The number 7 in this example is specifying the length of the created array.

Referring to array elements

The individual values, or elements, of an array are referred to by an index.  An index is an int value in the range 0..length-1, where length is the length of the array.  Index 0 is the first element of the array.  Here is how to use index values to assign new values to the elements of an array:

temperatures[0] = 27.2;
temperatures[1] = 57.4;

After a value is stored in an array element it may be retrieved using the same syntax:

System.out.println("Day 1 temp: " + temperatures[0]);
System.out.println("Day 2 temp: " + temperatures[1]);

The length of an array can be obtained by inspecting the value of the special length instance variable defined for all array objects.  Note that the length of an array is fixed at the time it is created, and never changes.  A very common idiom for programs that use arrays is a loop that starts at the beginning of the array and does something for each element in the array.  For example, the following loop prints the value of each element of the temperatures array:

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

Bounds Checking

Valid array index values are ints in the range 0..length (where length is the length of the array).  So, what happens if we try to access an array element using an index outside of this range?

The answer is that Java raises an ArrayIndexOutOfBoundsException, which is a serious runtime error that, by default, terminates the program.  So, you must be careful to only use array index values that are inside the supported range.

Arrays and References

Arrays, like all objecs in Java, are accessed through references.  You can think of a reference as an arrow pointing to the array in memory.  Array variables store a reference to an array, not the array itself.  For this reason, it is possible for two array variables to refer to the same array, which may result in surprising consequences.

Consider the following sequence of statements:

double[] vec = new double[3];
vec[0] = 1.0;
vec[1] = -1.0;
vec[2] = .5;

double[] vec2 = vec1;

After executing these statements, variables vec and vec2 both refer to the same array:

Changes made through one reference are visible through the other.  For example, we can assign a new value to an array element through the variable vec2 and it will be visible through the reference vec:

vec2[1] = 6.5;
System.out.println(vec[1]); // prints "6.5"