CISC 1115
Introduction to Programming Using Java
Lecture #11
Arrays
Reading from the Text
Chapter 7
Motivation for Arrays
Consider the following program/method specifications:
- Prompt the user for numbers until a negative number is entered. Print the sum of the numbers.
- Prompt the user for numbers until a negative number is entered. Print the largest of the numbers.
- Use a running 'max' variable
- Prompt the user for numbers until a negative number is entered. Print the squares of the numbers.
- Use a single integer variable — once the square is calculated and printed, the integer variable can be reused (i.e., its value overwritten)
In all of the above cases:
- once you've process the number (adding it to the total, comparing it to the 'max' variable,
printing its square), you can discard it and move on to the next number.
- This means you can use a single variable-- when the next value is assigned to the variable,
the previous value will be overwritten-- which is fine.
Now, consider the following programs/methods specifications:
Some number-crunching programs:
- Prompt the user for numbers (header, trailer, or eof). Print the numbers entered in reverse.
- Prompt the user for numbers (header, trailer, or eof). Print the numbers in sorted order.
- Read in a list of numbers, print out any duplicates.
- Read in a list of numbers, printing out the number that appears most often.
- Prompt the user for numbers (header, trailer, or eof). Print the 20 largest numbers.
How about some transcript-related programs?
- Read in the courses a student has taken from a transcript file. Print out the courses in most-recent-first order.
- Read in the courses a student has taken from a transcript file. Print the courses in alphabetical order.
- Read in the courses a student has taken from a transcript file. Print out whether the student has repeated any course.
- Read in the courses a student has taken from a transcript file. Print out the department in which the student has taken
the most courses.
How about some payroll-related programs?
- Read in employee data from a file and print them out in most-recent-hired-first order.
- Read in employee data from a file and print the employees in alphabetical order.
- Read in employee data from a file. Print out all departments with less than 2 employees.
- Read in employee data from a file and print out the department with the most employees.
In all these cases, we need to hold onto to ALL the numbers until we've finished our processing.
Consider the above programs in the context of
- 2 or 3 numbers
- 10 numbers
- 100 numbers
Not only is declaring 100 numbers horrible, but the subsequent logic is equally or more horrible.
What we need is some way of working with a bunch of numbers at once.
This involves being able to:
- avoid having to individually declare variables for each of the values
- avoid having to duplicate logic for each of the variables
Arrays
Basic definition
An array is a sequence of items, all of the same type, each of which
is referred to by its position in the sequence.
0 1 2 3 4 5 6 7 8 9 -> index
+---------------------------------------+
| 2 | 6 | 4 | 9 | 1 | 3 | 8 | 7 | 0 | 6 | -> element value
+---------------------------------------+
- A sequence in which all items are of the same type is called homogeneous
- The individual items are called elements
- The position of the element is called the index or
subscript
Arrays in Java
Arrays in Java are objects in the sense that they must be created.
Tasks we will be performing with arrays
- Declaring
- Creating
- Initializing/filling
- Accessing/Modifying elements
- Iterating/Traversing
- Passing as parameters and returning as values of methods
Revisiting Types, Values, and Variables
The following terminology and categories are Java-specific; many other object-oriented languages use similar or analogous terms.
Primitives
- Primitive types are those that have a direct representation in the hardware of the machine: numbers (integers and floating point), booleans, characters.
- A primitive value is a value of one of the primitive types:
17
, 12.3
, true
, 'A'
- Primitive values are only manipulated by
operators
: +
, *
, !
, &&
, etc..
- A primitive variable is a variable declared to hold a primitive value:
int i
, double d
, booleab b
, char c
- The value is contained directly within the memory location associated with the variable.
References
- Anything that is not a primitive type is a reference type; specifically, arrays and classes are reference types.
- Another way of looking at it, reference types are built up from the primitive types; e.g.
arrays of
int
(you will also learn that classes are built up from one or more simpler
types).
- So you could think of the primitives as the simpler types, and the reference types as the more complex/sophisticated ones.
- You could also think of the primitives as being the types that come built into the machine, while arrays and classes
are defined by (system and application) programmers.
- These characterizations are not strictly correct, but they are not harmful, and they are somewhat helpful.
- Reference values (analogous to primitive values) are values of reference types; i.e., array objects and class objects
- Any reference value in Java is said to be an object.
- Unlike primitive values, reference values are never manipulated using operators
- The one exception to this is the
+
concatenation operator for strings.
- Arrays are only manipulated using the subscript operator and the
length
field (see below).
- Again, this is not strictly true, but we can treat it as such for our purposes
- Classes (actually their objects) have methods invoked on them.
- Reference (and primitive) types may be passed as arguments/parameters to method, and returned as the value of a method; again
see below.
- Reference variables are different than primitive variables in that they do not contain the actual value of the array or object; rather they contain
a reference to the object they refer to.
References, Objects, Aliases
- Assigning one reference variable to another reference variable merely copies the reference to the existing object
String name1 = "Weiss";
String name2 = name1;
- In the above example, the
"Weiss"
is an anonymous String object (implicitly) created by the system (i.e.,
using a string literal causes the system to "automatically" create a String
object of that value.
- In the next case, we avoid the creation of the anonymous reference:
String name1 = new String("Weiss");
String name2 = name1;
- There's still only one object here;
name1
and name2
both contain references to it
- For all practical purposes, the above two cases are the same
- Variables that refer to the same object (as in the above two cases) are called aliases.
new
is the only way of creating objects; in the following code, we are creating two separate String objects (with the same
value):
String name1 = new String("Weiss");
String name2 = new String("Weiss");
- In this example, there are two separate, distinct objects created
- We say they are equal but not identical
- we typically define equality as representing the same value
- what representing the same value means is up to the designer of the class
- In the above
name
example, equality means having the same characters in the String representing one's name
- We sometimes speak of semantic equality, i.e., the equality of the to
objects is based upon the meaning of the objects — what it means for two objects of the class to be equal
- the method
equals
is usually defined (by the class designer) to test for (semantic) equality):
name1.equals(name2)
- We thus see that objects can be equal but not identical, but they can't be identical and not equal
- In the following case, the objects are neither identical nor equal:
String name1 = new Name("Weiss");
String name2 = new Name("Arnow");
Declaring an Array
Arrays are declared in a manner somewhat similar to other variables
- a name and a type must be supplied
- In addition, an indicator that the variable will be referring to an array must be specified as well.
- This indicator is a pair of
[]
's)
int [] nums;
double [] averages;
String [] names;
Creating an Array Object
Like Scanner
s, PrintStream
s, and File
s, an array object must be created and assigned to
the declared variable.
- ... and like the above objects, we use the
new
keyword to perform the creation
- in some sense,
new
is an operator that accepts the (array or class) type to be created, allocates the appropriate amount of
memory, and returns a reference to the newly allocated object.
- we also specify the number of elements the array will contain
- we do this using similar notation to the declaration, i.e.,
[]
's, but this time we specify the
number of elements in the array object we are creating
- the
[]
notation parallels the way we will be referring to the individual elements of the array
(see below; this follows the philosophy of C/C++ which says that the declaration should be reminiscent of the
wy you use the variable.
The Number of Elements in an Array (length
)
Working with Individual Elements in an Array: Subscripting
An individual element can be accessed by specifying its position in the array
using the index or subscript
operator [].
nums[0]; // literal or constant
averages[i]; // variable
names[names.length-1]; // arbitrary expressions
- The index of the first element in Java is 0
- The last position of the array is thus array.length-1array.length-1
- 10 element array-- last position = 9
- 14 element array-- last position = 13
- Attempting to access an index outside the bounds of the array (i.e., < 0 or >= array.length)
results in an
ArrayOutOfBounds
exception being thrown.
Initializing
There are numerous ways of assigning values to the elements of an array:
Explicit assignment
- Assign values to the individual elements
Program 11.1
Write a program that declares and creates an array of 5 elements, uses explicit assignment to initialize
the elements to the values 12, 15, 27, 95, and 13
, and then prints the array.
- Advantages
- Total flexibility in what you're assigning
- Disadvantages
public class Program_11_1 {
public static void main(String [] args) {
int [] arr = new int[5];
arr[0] = 12;
arr[1] = 15;
arr[2] = 27;
arr[3] = 95;
arr[4] = 13;
for (int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
}
}
Using an Initializer
- Include an initializer in the declaration
Program 11.2
Same a Program 11.1 but uses an initializer.
public class Program_11_2 {
public static void main(String [] args) {
int [] arr = {12, 15, 27, 95, 13};
for (int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
}
}
- Advantages
- Total flexibility in what you're assigning
- More succinct than explicit assignment
- Disadvantages
- Can only be done as initialization (as part of the declaration)
Using the Index Variable of a for
Loop
- Use a loop (typically a
for
loop) and use an expression involving the index variable (say i
to assign to the i
th element.
Program 11.3
Write a program that declares an array of 25 elements, uses a for loop to initialize the elements to
0, 10, 20, 30, 40... , 240, and prints the array
public class Program_11_3 {
public static void main(String [] args) {
int [] arr = new int[25];
for (int i = 0; i < arr.length; i++)
arr[i] = i * 10;
for (int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
}
}
- Advantages
- Disadvantages
- Only works if the elements follow a pattern than can be expressed via some expression involving the index variable
Reading in the Elements
- Typically from a file, rather than the keyboard
- Usually don't know the number of elements in advance
- Straightforward when used with a header value; not so simple when used with a trailer value or eof
Program 11.4
Write a program that reads integers into an array from a file with a header value. After reading in the values,
print the array.
import java.io.*;
import java.util.*;
public class Program_11_4 {
public static void main(String [] args) throws Exception {
Scanner scanner = new Scanner(new File("numbers.text"));
int numInts = scanner.nextInt();
int [] arr = new int[numInts];
for (int i = 0; i < numInts; i++)
arr[i] = scanner.nextInt();
for (int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
}
}
- Advantages
- Disadvantages
- The disadvantages of using a header value: must know the count
More on Accessing and Modifying the Elements of an Array
lvalues and rvalues
We've seen that depending on the context, a variable represents either its value or its location; i.e., sometimes we are interested in accessing its value and
sometimes we are interested in replacing the value with a new value
- replacing the value involves overwriting it in the location associated with the variable, which is where the notion of location comes from.
- There are times we are interested in the value of a variable
- For example, given the variable
x
, in the following situations we are interested in the value of x
:
- when it appears as an operand of an arithmetic or relational operator, e.g.
x + 1
or z != x
- when it appears as the right hand operand of an assignment, e.g.
y = x
- when it appears as an argument to a method, e.g.
System.out.prntln(x)
- In general, when the variable appears as an operand of an expression, we are interesting in its value
- There are other times we are interested in the location of a variable
- when the variable of the left hand operand of an assignment; i.e., it appears on the left hand side of the
assignment operator, e.g.
x = 3
- In this case, we are assigning the value of the right-hand side to the variable
- The previous value of the variable is of no interest to us; we will be overwriting it
- In particular, when a variable appears on the right-hand side of an assignment, we are interested in its value, while when it appears as the operand on the left-hand side, we are interested in
its location
- We thus refer to the value of a variable as its rvalue (right-hand value), and its location as its lvalue (left-hand value)
Subscripted Array Elements as lvalues and rvalues
The above discussion applies equally to the elements of an array; i.e. there are times when we are interested in the value (rvalue) of arr[i]
and time were are interested in its location (lvalue). (They are the same contexts as for variables as presented above).
- Given the array
arr
and the subscripted element arr[i]
for the following, we are interested in the rvalue of
arr[i]
:
- when it appears as an operand of an arithmetic or relational operator, e.g.
arr[i] + 1
or z != arr[i]
- when it appears as the right hand operand of an assignment, e.g.
y = arr[i]
- when it appears as an argument to a method, e.g.
System.out.prntln(arr[i])
- In general, when the variable appears as an operand of an expression, we are interesting in its value
- There are other times we are interested in the location of a variable
- when the variable of the left hand operand of an assignment; i.e., it appears on the left hand side of the
assignment operator, e.g.
arr[i] = 3
- In this case, we are assigning the value of the right-hand side to the variable
- The previous value of the variable is of no interest to us; we will be overwriting it
- In particular, when a subscripted array element appears on the right-hand side of an assignment, we are interested in its value, while when it appears as the operand on the left-hand side, we are interested in
its location
- We thus refer to the value of a variable as its rvalue (right-hand value), and its location as its lvalue (left-hand value)
Traversing/Iterating Through an Array
The process of going through an array visiting each of its elements is known as traversal or iteration.
It is an essential component to array processing … the whole point of the array is to provide a way to maintain a large number of elements whose processing
is the same from one to another.
Arrays and for
Loops
The Enhanced For Loop
Arrays and Methods
Arrays as Parameters
Methods that have array parameters are no different than other methods.
- Approach is same as with non-array parameters: declare the parameter using the 'usual' declaration for the type:
void print(int [] arr) {…}
- We can then use the parameter as usual: …
void print(int [] arr) {
for (int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
}
void print(int [] arr) {
for (int e : arr)
System.out.println(e);
}
- … and call the method as usual:
int [] arr = {1, 2, 3, 4, 5};
print(arr);
We want to turn useful operations on arrays into methods:
- Find minimum of an array
- Find average of an array
- Find an element in an array
We'll present these in the Techniques lecture
Array Arguments vs Subscripted Array Element Arguments
Given
int [] arr = new int[100];
…
void f(int [] arr) {…}
void g(int x) {…}
f
is a method accepting an array as a parameter, while g
is a method accepting an int
as a parameter
- thus
f(arr)
and g(arr[0])
are fine, but
f(arr[0])
and g(arr)
are both compilation errors
Arrays as Return Values
Arrays can be returned as the value of a method, just like any other variable:
int [] copy(int [] arr) {
int [] result = new int[arr.length];
for (int i = 0; i < arr.length; i++)
result[i] = arr[i];
return result;
}
Methods That 'Modify' their Array Parameters
Changes made to the elements of an array in a method remain (persist) after the method returns to the caller
int [] fillArray(int arr[]) {
for (int i = 0; i < arr.length; i++)
arr[i] = 0;
}
- Note this is NOT a modification to the array parameter itself (a reference), but a modification to the array object
Passing a reference is NOT 'call-by-reference'
The Wrong Way
void createAndFillArray(int [] arr, int size) {
arr = new int[size];
for (int i = 0; i < arr.length; i++)
arr[i] = 0;
}
…
int [] a;
createAndFillArray(a);
- whatever value (some reference) is in
a
(the argument) is passed to arr
(the parameter) by value (i.e.,
the value is copied from a
to arr
arr
is assigned a reference to the new array object created in the method (new int[size]
)
arr
is a local variable; it (and its value) is destroyed upon exiting the method; this value is NOT passed pack to a
The Right Way to Achieve This
- The only way for the method to communicate directly back to the caller is via the return value
int [] createAndFillArray(int size) {
int arr[] = new int[size];
for (int i = 0; i < arr.length; i++)
arr[i] = 0;
return arr;
}
…
int [] a = createAndFillArray();