Relevant Reading Material
Arrays
Arrays in java provide mechanism to store a fixed size collection of values. The syntax of arrays in Java can be a little confusing.
Array Type
To define a variable that has an array type we need to use []
and the type of the element(s) to be stored in the array.
Integer[] grades; // an array that stores Integer objects
String[] names; // an array that stores String objects
Array initialization
We can initialize an array in place
Integer[] grades = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Or we allocate the space needed and then using the index operator to set each location in the array
Integer[] grades = new Integer[10]; // observer the use of `new` here even though there is no class
grades[0] = 0; // index starts at 0 not 1
grades[1] = 1;
grades[2] = 2;
grades[3] = 3;
grades[4] = 4;
grades[5] = 5;
grades[6] = 6;
grades[7] = 7;
grades[8] = 8;
grades[9] = 9;
Array access
Using the index operator.
What happens when we index something out of bounds?
Array length
Array objects provide methods and fields to their clients. length
is the field that stores the max capacity of the array
Integer[] grades = new Integer[10];
System.out.println("Array size is: " + grades.length); // prints 10 even though there are no elements
System.out.println(grades[0]); // What is the value at index 0?
Looping over arrays
The typical way to iterate over the array? for
loop!
Integer[] grades = new Integer[10]; // observer the use of `new` here even though there is no class
grades[0] = 0; // index starts at 0 not 1
grades[1] = 10;
grades[2] = 20;
grades[3] = 30;
grades[4] = 40;
grades[5] = 50;
grades[6] = 60;
grades[7] = 70;
grades[8] = 80;
grades[9] = 90;
for(int i = 0; i < grades.length; i++){
System.out.println("At index: " + i + " value is: " + grades[i]);
}
If you are not interested on the index or you are not performing computation that jumps around based on the index, there is a better (preferred) way to do this
for (Integer element : grades) {
System.out.println("value is: " + element);
}
We can turn any for
into a while
loop
int i = 0;
while (i < grades.length) {
System.out.println("At index: " + i + " value is: " + grades[i]);
i++; // equivalent to `i = i + 1;`
}
Looping, not just for arrays
Consider the following List
ADT
public interface List<X> {
public static <Y> List<Y> CreateEmpty(){
// ...
}
public X getFirst();
public Boolean isEmpty();
public List<X> getRest();
public List<X> add(X element);
}
We could loop over the list using a while
loop
List<Integer> grades = List.CreateEmpty().add(10).add(20).add(30).add(40);
while(!grades.isEmpty()) {
System.out.println("Element = " + grades.getFirst());
grades = grades.getRest();
}
// lets do it in a for as well
for(; !grades.isEmpty();) {
System.out.println("Element = " + grades.getFirst());
grades = grades.getRest();
}
Multidimensional arrays
We can have a matrix (2-dimensional) arrays.
Integer[][] matrix = new Integer[5][10]; // 5 arrays, each array with 10 elements
or even more dimensions
Integer[][][] t = new Integer[5][10][3]; // can you draw the structure?
The main
method in Java
So there is a special static method called main
that Java uses as the entry point to your code.
class Main {
public static void main(String[] args) {
// start executing my program here!
}
}
The argument to main
is an array of String
s that will hold the text passed to the JVM as extra arguments.
We will talk more about this later in the semester.
Java Primitive Types
Up to this point we were using reference types in our Java programs. There are also primitive types as well. These is a second category of types and values that the Java language lets us use.
-
int
,double
,float
,byte
,short
,long
- deal with numbers at different precision and range -
boolean
- primitive type fortrue
andfalse
-
char
- primitive type for a unicode character
The primitives have a corresponding reference type with the same name, but the first name is Capital! Java 8, can automatically take in an int
and turn it into an Integer
, this is called boxing. Similarly Java can take a reference type and turn it into a primitive, i.e., Integer
to int
, this is called unboxing. These two operations boxing and unboxing are collectively called autoboxing.
Generics only work for reference types. Also, sometimes, autoboxing might lead to ambiguous method calls when a class has a method with the same name and return type one version of the method uses a primitive type as an argument and the other a reference type.
public interface Adder{
void add(int x);
void add(Integer x);
}
As a rule of thumb you should use primitive types for data that you would like to capture to which you do not want to add behaviour/operations (methods). Typically you would like to "wrap" your primitive data with a reference type that captures the information in your problem domain.
class Person {
private Name name;
private Age age;
// methods elided
}
class Age {
private int age;
// methods elided
}
Lists with mutation
Lets try and implement this List
interface
public interface List<X> {
public static <Y> List<Y> CreateEmpty() {
// FIXME : needs an implementation
}
void prepend(X element);
void append(X element);
Integer size();
Boolean isEmpty();
X getElement(Integer index);
}
Lets implement void remove(X element)
as well.
Invariants
Can we keep track of the number of elements rather than counting them every time we are called?
We can keep a counter field that we update accordingly when we add (prepend
or append
) and remove
.
Class Invariant is a boolean expression that captures what should always hold true about the state of this class' objects before the call to a public method and immediately after the return of a public method. The time spend executing the body of the method (including calling helper methods) we can temporarily break this invariant.
Another way to think of the invariant is a boolean expression that we logically-and with the public methods' pre
and post
conditions.