Lesson 8.2 Processing Arrays
Processing Array Elements
This lesson will teach you how to process primitive arrays. First, you'll learn about storing and retrieving values in individual elements. Then, you'll learn how to process an entire array using a loop. Finally, you'll learn how to use methods that take arrays as arguments and how to write methods that produce array values.
To access an individual array element, you use the name of the array and add the subscript of the element you want to access in brackets following the name. Using this "compound" name, you can store new values in individual elements, or retrieve values from individual elements in the array.
Storing and Retrieving Values
For example, how would you store a new grade into the first element of gradeAR? Simple, you just do this:
In the same way, you can retrieve a value simply by using the subscripted variable name in an expression, like this:
int i = 0;
char current = gradeAR[i]; |
As you can see, when you use the subscripted name, the subscript (index) does not have to be a constant. You can use a variable or an expression as long as the result is an int value.
Although the type of the variable gradeAR is char[] (array of char), the type of the individual elements (gradeAR[0]) is plain-old char. You can treat each of the individual elements just as if it were a simple char variable.
Errors
Of course, this means that you have to remember to store only compatible values. If you try this, for instance,
you'll get a compiler error because you can't store a double value in a char variable.
A more common mistake is to create a runtime error by trying to store values [or retrieve values] from an element outside the bounds of the array. The most common cause of this error is forgetting that a five-element array, like gradeAR, has subscripts that range from zero to four. The expression
will throw an ArrayIndexOutOfBounds exception. This exception is an unchecked exception, so you don't have to place all of your array processing inside a try-catch block. You may, however, use try-catch to intercept this exception.
The length Field
Every array variable has a public field named length that you can use to discover how many elements are in the array. There are two things you should remember when using the length field:
- In an array, length is a field; in a String, length() is a method. It's easy to get the two of these confused.
String s = "Abara";
int lenS = s.length();
int lenA = gradeAR.length; |
- The length field stores the number of elements in the array, not the highest subscript number, which is always length-1.
gradeAR[length] = 'C'; // NO
gradeAR[length-1] = 'C'; // OK |
Using a Loop
The real advantage of arrays is that it allows you to apply the same processing to several different values by using a loop. For array processing, the for loop is the loop of choice because the array.length field creates a natural count bounds.
Here are a few examples of common loops and the situation where you'll use them.
Initializing an Array
If you need to initialize an array to a value other than zero, the initializer list is useful only for short arrays. Typing in even 25 or 50 values can get very tedious. Instead, you should use a for loop.
Here's an example that fills an array with random numbers:
|
Initializing a Random Array
|
// randAR is a double array
int len = randAR.length;
for (int idx = 0; idx < len; idx++)
{
randAR[idx] = Math.random() * len;
} |
Summing and Averaging
Summing, averaging, and counting are all common operations performed on arrays. Here's a loop you can use as an example when you have to perform these operations:
|
Summing, Counting and Averaging
|
// randAR is an initialized double array
// len is an int; assumed > 0
len = randAR.length;
double sum = 0.0, avg;
for (int idx = 0; idx < len; idx++)
{
sum += randAR[idx];
}
avg = sum / len;
// Count larger & smaller than avg
int larger = 0, smaller = 0;
for (int idx = 0; idx < len; idx++)
{
if (randAR[idx] < avg)
smaller++;
else if (randAR[idx] > avg)
larger++;
}
Try it yourself. Press the button and the SumAvgCount applet will initialize randAR using Math.random(), and then sum and average the results. As a bonus, it displays the percentage of numbers that are larger than, and the percentage of numbers that are smaller than the average.
|
Arrays and Methods
An array variable and the actual array where the array elements are stored are two different things. Although you cannot use an actual array as an argument to a method, you can pass array variables as arguments. You can also write methods that return array variables.
Let's see how that works.
Passing Array Variables
To pass an array variable as an argument, you first have to know how to write the formal argument.
Suppose that you write a method that sums the values in a double array [of any size], and returns the result as a double. Using the method, you can calculate the sum of the elements in the double array nums like this:
| double sum = sumAR(nums); |
When you write the method, the formal argument can't be a double; it must be an array of double. You write the method header like this:
| double sumAR(double[] ar) { } |
or like this:
| double sumAR(double ar[]) { } |
Here's the entire method.
|
Passing an Array Variable to a Method
|
double sumAR(double[] ar)
{
double sum = 0.0;
for (int i = 0; i < ar.length; i++)
{
sum += ar[i];
}
return sum;
} |
Notice that you can pass any size array to this method because it uses the array's length field as the loop bounds.
Modifying an Array
When the array variable nums is passed to the method sumAR(), a copy of nums is made, as you can see in this figure.
[click the figure to enlarge]
This is the same as with any variable. All method arguments are passed by value in Java--a copy of the actual argument is made for use inside the method.
However, with an array, the effect might not be what you expect. Because both nums and ar refer to the same actual array, any changes made to the elements of the array ar inside the method sumAR() affect the elements of the array nums as well. To reiterate, both nums and ar refer to the same actual array.
Here's a method that shows you how that works. The method doubleAR() doubles every element in the array passed to it:
|
Modifying Array Elements in a Method
|
void doubleAR(double[] ar)
{
for (int i = 0; i < ar.length; i++)
{
ar[i] *= 2;
}
} |
Array Copies
Most of the time, this behavior is no problem; it saves memory and increases performance, because only array variables are passed around, not entire arrays.
Occasionally, though, you have to pass an array to a method that changes the original array, but you still need to preserve the original array contents as well.
This is not hard to do, but you have to know how to do it. You can't use the assignment operator like this:
double[] copy = nums;
doubleAR(copy); |
If you do this, instead of having two array variables referring to the same actual array [ar and nums], you now have three: ar, nums, and copy. Any changes made to the elements of ar in the method doubleAR(), affect both copy and nums as well.
Since the assignment method obviously doesn't work, how do you do this correctly? There are two ways:
- Write a loop and copy each element one-by-one.
- Use the System.arraycopy() method.
System.arraycopy()
As you might expect, the second method is preferred because it is written in highly optimized native code as part of the JVM for your platform. Using System.arraycopy() will almost always be faster than writing your own loop.
Unfortunately, a first look at its documentation can be daunting. System.arraycopy() takes five arguments:
- The name of the source array [copy from here]
- The offset to copy from [use 0 to start at the beginning]
- The name of the destination [copy to here]
- The offset to copy to [use 0 to start at the beginning]
- The number of elements to copy
The JVM will throw an exception if any of these arguments are wrong. Fortunately, when copying an entire array, it's not hard to get them right. Simply do this:
double[] copy =
new double[nums.length];
System.arraycopy(nums, 0,
copy, 0,
nums.length); |
That wasn't too hard, was it? We can make it easier though. How would you like to simply write this?
| double[] copy = dupeAR(nums); |
It's easy if you know how to write methods that return array variables.
Returning Array Variables
Java allows you to easily write methods that return array variables. As with passing array variables to a method, the only trick is in knowing how to declare the return type.
Suppose, for instance, you wanted to write a method that returned an array of random integers. Your method header would look like this:
| int[] makeRandom(int howMany) {} |
Notice that the return type is declared as int[] and not int.
Once you've figured out how to write the return type, the next question that arises is "How do you get the array variable to return?"
The answer is simple--you create a local array variable inside your method, and then return the local variable.
Here's the rest of the makeRandom() method that shows you how:
|
Returning an Array Variable from a Method
|
int[] makeRandom(int howMany)
{
// 1. A random number generator
Random rand = new Random();
// 2. A local array variable
int[] ar = new int[ howMany ];
// 3. Fill it with random numbers
for(int i = 0; i < ar.length; i++)
ar[i] = rand.nextInt();
// 4. Return a copy of the local ar
return ar;
} |
If the "light has gone on", you probably now realize how you could "wrap up" a call to System.arraycopy() in a dupeAR() method, to make copying entire arrays a whole lot easier.
The dupeAR() method would look like this:
|
A Note to C/C++ Programmers
|
| If you've ever programmed in C or C++, returning a local array variable probably makes you a little woozy! Much of a C/C++ course is spent teaching you to avoid doing this at all possible costs.
In C/C++, if you return a local array from a function, the local array is "gone" by the time the function returns. In Java, however, all you are returning is a variable that refers to the array. The actual array is "out there" somewhere.
Of course, you could do the same thing in C/C++ by creating an array on the heap and then returning a pointer to it, but then you risk a memory leak. Because Java has automatic memory management, that's not an issue here. |
Something to Talk About
Download and read SumAvgCount.java. What code would you add to calculate the largest and smallest numbers, in addition to the percentages calculated above?
Please continue to the next section of this lesson.
|