Intro to Java Arrays

CSCI 1933 – Introduction to Algorithms and Data Structures
Adriana Picoral

Arrays

Arrays in Java are like lists in Python, except arrays are more restrictive than lists:

  • Java arrays hold elements of one type only (homogeneous typing) – lists in python can hold elements of different types
  • Java arrays have fixed length, which is determined when array constructor is called – you can keep appending to python lists “forever”

Indexing is the same – myArray[0], indices start at zero.

Java array syntax

To create a new java array, you need to specify the type of elements for the array and its size (in this case the size or length of the array is 200):

int[] myIntArray = new int[200];

We can instead of size, specify the elements in the array:

int[] myIntArray = new int[]{2, 3, 2, 4, 5};

Arrays are objects, with attributes:

System.out.println(myIntArray.length);

Iterating over arrays

We can iterate over the array indices:

for (int i = 0; i < myIntArray.length; i++) {
  // do something with variable i
}

Or iterate over the array items (for each)

for (int n : myIntArray) {
  // do something with variable n
}

Practice – ArrayCalculator class

Download TestArrayCalculator. Submit your ArrayCalculator.java to Gradescope

  • Private instance variable that is an array of doubles called numbers
  • Constructor that takes in an array of doubles as argument
  • Getter for numbers, and setter for the numbers array that take as argument an index
  • Operator methods: add (add all numbers), subtract (subtract all numbers)
  • Override toString() and equals(Object o) methods

A solution

public class ArrayCalculator {
    private double[] numbers; // only need to declare type, no initialization

    // constructor
    public ArrayCalculator(double[] numbers) {
        this.numbers = numbers;
    }
    
    
    // getter
    public double[] getNumbers() {
        return numbers;
    }

    // setter
    public void setNumber(int index, double value) {
        if (index >= 0 && index < numbers.length) numbers[index] = value;
    }

    // operators
    public double add() {
        double result = 0;
        for (double n : numbers) result += n;
        return result;
    }

    public double subtract() {
        double result = 0;
        for (double n : numbers) result -= n;
        return result;
    }


    @Override
    public String toString() {
        String output = "";
        for (double n : numbers) {
            output += n + " ";
        }
        return output.trim();
    }

    @Override
    public boolean equals(Object obj) {
        // check if same object
        if (this == obj) return true;

        // check if argument is an instance of this class
        if (!(obj instanceof ArrayCalculator)) return false;

        // type cast to this class to access methods
        ArrayCalculator another = (ArrayCalculator) obj;

        // check length
        if (numbers.length != another.getNumbers().length) return false;

        // check each item
        for (int i = 0; i < numbers.length; i++) {
            if (numbers[i] != another.getNumbers()[i]) return false;
        }

        return true;
    }
}

OOP thinking

When writing a method, you need to consider:

  • What are the instance variables of the class?
  • What arguments do I need to pass to my method?

Example: write a method that multiplies every element in the numbers array by some double multiplier, call it multiplyBy – What argument do we need to pass to this method?

Passing array objects to methods

Write a static method called doubleNumbers that takes in a double array and modifies it by multiplying each item in the array by 2.

Compare method types

We wrote an instance method and a static method (function):

public void multiplyBy(double multiplier) {
  for (int i=0; i < numbers.length; i++) {
    numbers[i] *= multiplier;
  }
}

public static void doubleNumbers(double[] anotherNumbers) {
  for (int i=0; i < anotherNumbers.length; i++) {
    anotherNumbers[i] *= 2;
  }
}
  • What are the differences between these two methods?
  • Which one is “better”? (How do we assess quality?)

Practice

Write a method (and test it) to reverse the elements in the instance array. Assume the array is completely filled.

  • Should this method be an instance method, or a class method (static, function)?

Submit your ArrayCalculator.java solution with reverse() to gradescope

Changing the array size

What if we want to implement an append(double value) method?

  • What happens when an array is “full”?
  • How to “resize” an array?

Solution

public class ArrayCalculator {
    private double[] numbers;
    private int size;
    private int length;

    public ArrayCalculator(double[] numbers) {
        this.numbers = numbers;
        size = numbers.length;
        length = numbers.length;
    }

    public double[] getNumbers() {
        double[] outputArray = new double[length];
        for (int i=0;i < length;i++) {
            outputArray[i] = numbers[i];
        }
        return outputArray;
    }

    public double getAt(int index) {
        if (index >= 0 && index < length) return numbers[index];
        throw new RuntimeException("index out of range");
    }

    public int getLength() {
        return length;
    }

    public void setNumber(int index, double value) {
        if (index >= 0 && index < length) numbers[index] = value;
    }

    public double add() {
        double result = 0;
        for (double n : numbers) result += n;
        return result;
    }

    public double subtract() {
        double result = 0;
        for (double n : numbers) result -= n;
        return result;
    }

    public void multiplyBy(double multiplier) {
        for (int i=0; i < numbers.length; i++) {
            numbers[i] *= multiplier;
        }
    }

    public static void doubleNumbers(double[] anotherNumbers) {
        for (int i=0; i < anotherNumbers.length; i++) {
            anotherNumbers[i] *= 2;
        }
    }

    public void reverse() {
        double[] newNumbers = new double[length];
        for (int i=0; i < length; i++) {
            newNumbers[i] = numbers[length-1-i];
        }
        numbers = newNumbers;
        // alternative, call the recursive version of reverse:
        // numbers = reverse(0, new double[length]);
    }
    
    private double[] reverse(int index, double[] newNumbers) {
        if (index == length) return newNumbers;
        newNumbers[index] = numbers[length-1-index];
        return reverse(index+1, newNumbers);
    }

    public void append(double value) {
        if (size == length) resize();
        numbers[length] = value;
        length++;
    }

    private void resize() {
        size *= 2;
        double[] newNumbers = new double[size];
        for (int i=0;i< numbers.length;i++) {
            newNumbers[i] = numbers[i];
        }
        numbers = newNumbers;
    }

    @Override
    public String toString() {
        String output = "";
        for (int i=0;i < length;i++) {
            output += numbers[i] + " ";
        }
        return output.trim();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;

        if (!(obj instanceof ArrayCalculator anotherCalc)) return false;

        if (length != anotherCalc.getLength()) return false;

        for (int i = 0; i < length; i++) {
            if (numbers[i] != anotherCalc.getAt(i)) return false;
        }

        return true;
    }
}

Looking Back

  • How are Python lists and Java arrays similar? How are they different?
  • Changes made to an array when passed to a method will change the original array. Why?
  • Changes made to basic types when passed to a method will not change the original value. Why?
  • When do we write static methods (functions) vs. instance methods?
  • How do we decide what arguments to pass to methods?