CSC8313
OBJECT ORIENTED PROGRAMMING
The ArrayList collection – Lecture 4
1 1
LECTURE 4 OBJECTIVES
Understand the benefits of using the ArrayList
class and some of its common methods.
Appreciate the use of the for-each loop, auto-
boxing and wrapper classes.
Take a first look at the forEach method and
Stream API.
2 2
ARRAYS CAN BE USED AS FIELDS WITHIN CLASSES TO
MAKE OTHER DATA STRUCTURES
Stack<T>
A stack of some
- stackArray : T[] type, T
The data
store
- top : int
Index of last
+ push(T) element
+ pop()
Typical stack
methods + top() : T
+ isEmpty() : boolean
3
THE ARRAYLIST CLASS
An array's size is fixed upon creation – we do not always
know how many items we wish to store in a collection.
The ArrayList class provides a more powerful data structure
within the Collections framework.
It can grow and shrink dynamically as needed.
It also provides a useful set of methods through its public
interface for common tasks such as inserting or removing
elements.
4
JAVA.UTIL.ARRAYLIST<T>
The Java SDK provides a class ArrayList, placed within the
java.util package that:
encapsulates an array of objects.
provides methods for maintaining a general purpose list.
The type <T> of object stored in the internal array can be
instantiated during the creation of the ArrayList.
This is known as a Generic Type parameter, meaning the
ArrayList may store elements of any reference type.
5
EXAMPLE OF A SIMPLE GENERIC CLASS
public class Pair<T, S> {
private T first;
private S second;
public Pair(T f, S s) {
first = f; second = s;
}
public T getFirst() {
return first;
} //…etc
6
USING THE GENERIC CLASS
//using class with different types…
Pair<String, Integer> fruit = new
Pair<String, Integer>("Apple", 1);
String x = fruit.getFirst();
Pair<Die, Die> pod = new
Pair<Die, Die>(new Die(), new Die());
pod.getSecond().roll();
//…etc
7
INSTANTIATING AN ARRAYLIST
Examples of instantiating an ArrayList with different type
parameters:
ArrayList<String> words = new ArrayList<String>();
ArrayList<Name> friends = new ArrayList<Name>();
ArrayList<Integer> marks = new ArrayList<Integer>();
But NOT ArrayList<int> - <T> must be a reference
type to be compatible with generics.
8
JAVA 7 DIAMOND SYNTAX
Java 7 has introduced a convenient syntax enhancement
for declaring array lists and other generic classes.
You need not repeat the type parameter in the constructor.
ArrayList<String> names = new ArrayList<String>();
ArrayList<String> names = new ArrayList<>();
The shortcut is called 'diamond operator' as the empty
brackets <> look like a diamond shape.
9
WRAPPERS AND AUTO-BOXING
Java has a wrapper class for each of the primitive types,
e.g. Integer for int, Double for double, etc.
Suppose we have an ArrayList of type Integer:
ArrayList<Integer> marks = new ArrayList<>();
Both of these operations are syntactically valid:
marks.add(5); //box
int m = marks.get(0); //unbox
A procedure called autoboxing and unboxing ensures the
automatic conversion of a wrapper class type to its
corresponding primitive type and vice versa. 10
ARRAYLIST METHODS (SEE API FOR MORE)
Can add elements to the list: add(x), add(i,x)
Can remove elements from list: remove(x), remove(i)
Can retrieve an element at index ‘i’ using: get(i)
Can replace an element, x, at index ‘i’ using: set(i, x)
Can check if the list is empty: isEmpty()
Can check the number of objects currently stored: size()
Can clear all elements within the list: clear()
11
ARRAYLIST: SIZE V CAPACITY
When an ArrayList is first created it receives an initial
capacity of 10 elements.
The underlying array automatically re-sizes at runtime if
the original capacity has been reached.
Therefore the size of the ArrayList represents the
number of objects currently stored.
The capacity represents the number of objects that can
be stored until it needs to expand, i.e. The JVM will
reallocate more memory and copy elements over.
12
USING THE LIST INTERFACE TYPE
An ArrayList object can be stored in a variable container of
type List – this is a common interface for all list types in the
Java collections framework:
List<String> list = new ArrayList<>();
ArrayList<String> list = new ArrayList<>();
The former restricts you to use only methods defined in the
interface, but allows the data structure to be changed.
An ArrayList can be changed to any other class that
implements the List interface, e.g. LinkedList, Vector,
etc.
13
LIST INTERFACE’S IMPLEMENTING CLASSES
View the API at:
https://docs.oracle.com/javase/8/docs/api/java/util/List.html
List<String> list = new ArrayList<>();
List<String> list = new LinkedList<>();
List<String> list = new Vector<>();
14
ARRAYLIST: A COLLECTION OF REFERENCES
When we instantiate an ArrayList of different types, how
much memory is allocated? e.g.
List<String> s = new ArrayList<>();
List<Integer> i = new ArrayList<>();
List<BigObject> o = new ArrayList<>();
The same memory would be allocated for each ArrayList,
that is enough memory to store 10 object references.
BigObject objects may require a huge amount of memory,
but the ArrayList will only store references, not the actual
objects themselves.
15
USING A FOR-EACH LOOP
Like a for-i loop but has a simplified syntax. It can be used
with an array or a collection class (e.g. ArrayList).
The loop does not require a counter; it will traverse through
each element within the collection, providing direct access
without needing to worry about index.
Syntax is:
for(elementType variable : collection) {
statements;
}
16
FOR-EACH: USE AND LIMITATIONS
for(int x : myArray) {
System.out.println(x);
}
It is only possible to access values stored within each
element when using a for-each loop, so you cannot modify
them, as with a for-i loop, e.g. a[i] = -99.
As you have no access to the index, it is not possible to
know where the array value is within the collection.
You cannot process a subset of elements, e.g. all odd.
17
ARRAYLIST OF OBJECTS: EXAMPLE
Example of an ArrayList of 3 String objects, that are
traversed using the for-each loop and output in upper
case:
List<String> fruit = new ArrayList<>();
fruit.add("apple");
fruit.add("pear");
fruit.add("grape");
for (String s : fruit) {
System.out.println(s.toUpperCase());
}
18
ARRAYLIST OF OBJECTS: EXAMPLE (2)
Using a for-each loop you cannot update the object
reference stored in an ArrayList, but you can modify the
internal data of an object, e.g. Setting quantity:
List<Order> orders = new ArrayList<>();
orders.add(new Order("Beans", 55, 7));
orders.add(new Order("Milk", 99, 20));
//etc
for (Order o : orders) {
o.setQuantity(30);
totalcost += o.getCost();
}
19
THE JAVA 8 FOREACH METHOD
The addition of functional programming constructs in the
8th release of Java has provided another way of
processing collections – the forEach method.
The syntax is.
collection.forEach(lambda expression);
orderList.forEach(od -> od.toString()); //an example
Aggregate operations, such as forEach, process elements
from a stream and cleanly separate the logic of iteration
and element execution, allowing parallelism.
20
WHAT IS A LAMBDA EXPRESSION ?
Traditionally, a method would accept arguments, representing
either primitive or reference type data.
With lambdas we can treat functionality as a method argument, or
code as data, i.e. pass in a method.
For one line method bodies, you can omit both the braces {} and
the return keyword. Parameter types can often be inferred from
the context of the surrounding code.
orderList.forEach((Order od) -> { od.getCost(); });
orderList.forEach( od -> od.getCost()); //type Order inferred
21
STREAMS CAN OPERATE ON COLLECTIONS
A stream represents a sequence of elements on which one
or more operations can be performed.
A stream pipeline consists of:
a sequence of aggregate operations containing a source (e.g. a
list).
zero or more intermediate operations (e.g. filter, map) that
produce a new stream.
a terminal operation that produces a non-stream result (e.g.
forEach, sum, count, anymMatch).
22
EXAMPLES USING STREAMS
int total = orderlist.stream().mapToInt((Order od)
-> { return od.getCost(); }).sum();
int total = orderlist.stream().mapToInt( od ->
od.getCost()).sum(); //with valid omissions
----------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------
boolean exists = register.stream().anyMatch(nm ->
nm.getFamilyName().equals("Smith"));
----------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------
register.stream().filter(n ->
n.getFirstName().length() < 5).map(n ->
n.getFamilyName()).forEach(f ->
System.out.println(f)); //what will print out?
23
HOW DO I CHOOSE THE FORMAT OF A
LAMBDA EXPRESSION?
A lambda expression can be substituted wherever a
functional interface is expected:
⚫A functional interface contains only a single abstract method.
⚫The lambda expression should match the parameter-list and return
type of the abstract method.
Example – the Predicate<T> interface has a method
test, i.e.
public boolean test(T t)
The stream filter operation accepts a predicate as its
argument. The type parameter T is substituted with the
incoming element type of the stream.
24
A PREDICATE LAMBDA EXPRESSION
We could write:
register.stream().filter((Name n) -> {
return n.getFirstName().length() < 5;})...
//or omit parameter type, return and braces
register.stream().filter(n ->
n.getFirstName().length() < 5)...
In each case, you are defining a lambda expression that
accepts an incoming Name object and returns a boolean
value. This will run on each stream element.
This clearly matches the signature of the Predicate
interface's test method.
25
LAMBDA REQUIRED BY FOREACH METHOD
To execute the forEach method we may write:
register.forEach(n ->
System.out.println(n.getFullName());
By viewing the API we see that the forEach method
accepts an argument of type consumer.
The Consumer<T> interface has a method accept, i.e.
public void accept(T t)
Quite simply, it accepts the incoming element type and
performs a given operation, without having to return
anything.
26
CHOOSING AN ARRAYLIST OR ARRAY?
Use an array if:
the size of the collection never changes.
you collect a long sequence of primitive type values and you are
concerned about efficiency (auto-boxing takes time).
Otherwise, use an ArrayList!
We will rarely use arrays during the rest of the module
and instead will use the ArrayList class, or other
collection classes, e.g. maps or sets.
27
OVERRIDING EQUALS – COMMON MISTAKE
A common mistake is to set the parameter type to be
the same as the class type, instead of Object, e.g.
public boolean equals(Name obj) {
If you do this, it will not actually override the
equals(…) method inherited from the Object class. It
will instead overload it. To be safe, use the @Override
annotation, e.g.
@Override
public boolean equals(Object obj) {
28 28
NOTE: OVERLOADING METHODS
An overloaded method is simply a method with the same
name that is declared more than once with a different
parameter-list.
For example, there are actually lots of different versions
of println(…). Think about an ArrayList, there are
two add methods: add(E) & add(int, E).
Overloaded methods are completely different to
overridden methods, but it is worth being aware that if
you do not override a method, you will likely be
overloading it instead!
29 29
SO WHAT IS THE ISSUE?
If we accidently overload equals(Object) with our
own version then what is actually the issue?
After all… we can still use it to check two of our custom
Name objects for equality.
The issue is that other classes/methods may also use
equals(…) and they would use the inherited
equals(Object) version, not your own.
An example of this is the contains(…) method
belonging to the ArrayList class.
30 30
ARRAYLIST CONTAINS(...) METHOD
When you have an ArrayList instance populated with
objects of a given type, you can test if the list already
contains an object.
The contains(…) method uses the equals(…)
method to test objects for equality.
ArrayList<Name> names = new ArrayList<>();
names.add(new Name("Joe", "Bloggs");
Name n = new Name("Joe", "Bloggs");
if ( names.contains(n) )
//will evaluate true if the Name class
overrides equals(…)
31 31
ARRAYLIST REMOVE(OBJECT) METHOD
The equals method is used by other ArrayList methods
such as remove, which removes the provided object.
For example
//add lots of name objects
Name n = new Name("Joe", "Bloggs");
if ( names.remove(n) )
//name found and successfully removed
else
//name not found
32 32
SUGGESTED READING
Big Java : Early Objects (5th Ed.) Cay, S. Horstmann
⚫Chapter 6: Arrays and Array Lists
⚫Section 2.9.2: The null Reference
Java Tutorials hosted by Oracle
⚫http://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html
⚫http://docs.oracle.com/javase/tutorial/collections/implementations/list.html
For those interested in learning more about Streams e.g.
⚫http://docs.oracle.com/javase/tutorial/collections/streams/
⚫http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html
33 33
34
EXERCISE 4
4.1 Copy the Name class that you have created in exercise 3.2 to your
current Java project and then create a new class called "NameListTest“.
Create an ArrayList of type Name (i.e. List<Name> register = new
ArrayList<>();).
a. Add names of five students to the register.
b. Use the forEach method to output the full name of every Name element
in the list. You should use lambda expression.
c. Using stream().filter(...) create a stream pipeline to output names that
are of a length greater than 10 characters.
35 35
EXERCISE 4.2
4.2 Copy the Order that you have created the Lab to your current Java project and then
create a new class called “OrderListTest“.
Create an ArrayList of type Name (i.e. List<Order> orderList = new
ArrayList<>();).
a. Add five different orders to the orderList.
b. Use the forEach method to output the id and cost of each order. You should use lambda
expression.
c. Using stream().mapToInt(… ).sum(); create a stream pipeline to output the total
cost of the orders in the orderList.
.
Submission:
Upload your java files
NameListTest.java and OrderListTest.java to the google drive folder that your
have shared with the email ([email protected]) by 10:00 am, 7th July, 2021.
36 36