Processing math: 100%
+ - 0:00:00
Notes for current slide
Notes for next slide

CSCI-UA 102

Data Structures


Lists (Part 2)

Instructor: Joanna Klukowska


Copyright 2020 Joanna Klukowska. Unless noted otherwise all content is released under a
Creative Commons Attribution-ShareAlike 4.0 International License.
Background image by Stewart Weiss

1/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

2/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
3/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
  • ArrayList<E> = an array (that can store any type of data) + operations that make everything easy
4/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
  • ArrayList<E> = an array (that can store any type of data) + operations that make everything easy

Where did the pirate store his loot?

  • In an ArrayList, because ArrayLists can grow!

Dana L., 2018

5/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
  • ArrayList<E> = an array (that can store any type of data) + operations that make everything easy

Where did the pirate store his loot?

  • In an ArrayList, because ArrayLists can grow!

Dana L., 2018

  • So how do they grow? And can we make it efficient?
6/46

Growing the Array

7/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

8/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

9/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

growing an array

  • creating a new array and copying the data is fairly easy, but how much more room should we allocate?
    • if it is too large, then we have an array that is much too big for our data
    • if it is too little, then we might need to do this again, and again, and again
10/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
11/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
  • where does the O(N) time come from in the above function
12/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
  • where does the O(N) time come from in the above function
    • arraycopy() is linear, it runs in time proportional to the size of the array
    • but it is in practice faster than executing:
      for (int i = 0; i < size; i++)
      bigger[i] = array[i];
13/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
  • where does the O(N) time come from in the above function
    • arraycopy() is linear, it runs in time proportional to the size of the array
    • but it is in practice faster than executing:
      for (int i = 0; i < size; i++)
      bigger[i] = array[i];
  • so what is the performance of this function overall (not just sometimes O(1) and sometimes O(N))?
14/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
15/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
16/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
17/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
18/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)
19/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

20/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

21/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

The add at the end (append) operation for an ArrayList has an amortized time performance of O(1) as long as the array size in increased by a multiplicative factor, not an additive factor.

22/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

The add at the end (append) operation for an ArrayList has an amortized time performance of O(1) as long as the array size in increased by a multiplicative factor, not an additive factor.

  • this means that we need to multiply the current size of an array by a constant to get the size of the bigger array: if N starts as 10, and the multiplicative constant is 2, the sizes will be: 10, 20, 40, 80, 160, ...
23/46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

The add at the end (append) operation for an ArrayList has an amortized time performance of O(1) as long as the array size in increased by a multiplicative factor, not an additive factor.

  • this means that we need to multiply the current size of an array by a constant to get the size of the bigger array: if N starts as 10, and the multiplicative constant is 2, the sizes will be: 10, 20, 40, 80, 160, ...

  • not add some constant to the current size: if N starts at 10, and the additive constant is 2, the sizes would be: 10, 12, 14, 16, 18, ...

24/46

Adding and Removing

25/46

© Joanna Klukowska. CC-BY-SA.

Adding at an Arbitrary Position

If we want to add an element to an arbitrary position in the list, we will need to shift the values that are there to the higher indexes to make room for it.

26/46

© Joanna Klukowska. CC-BY-SA.

Adding at an Arbitrary Position

If we want to add an element to an arbitrary position in the list, we will need to shift the values that are there to the higher indexes to make room for it.

27/46

© Joanna Klukowska. CC-BY-SA.

Adding at an Arbitrary Position

If we want to add an element to an arbitrary position in the list, we will need to shift the values that are there to the higher indexes to make room for it.

growing an array

This operation is O(N) because we need to shift the elements by one position to the right (higher indexes).

28/46

© Joanna Klukowska. CC-BY-SA.

Removing from an Arbitrary Position

If we want to remove an element from an arbitrary position in the list, we will need to shift the values in the higher indexes to fill in the whole that remains after removing an element.

29/46

© Joanna Klukowska. CC-BY-SA.

Removing from an Arbitrary Position

If we want to remove an element from an arbitrary position in the list, we will need to shift the values in the higher indexes to fill in the whole that remains after removing an element.

30/46

© Joanna Klukowska. CC-BY-SA.

Removing from an Arbitrary Position

If we want to remove an element from an arbitrary position in the list, we will need to shift the values in the higher indexes to fill in the whole that remains after removing an element.

growing an array

This operation is O(N) because we need to shift the elements by one position to the left (lower indexes).

31/46

Comparing ArrayList<E> Objects for Equality

32/46

© Joanna Klukowska. CC-BY-SA.

equals method specification

The equals method in the ArrayList<E> class is inherited from the AbstractList<E> class.


33/46

© Joanna Klukowska. CC-BY-SA.

equals method specification

The equals method in the ArrayList<E> class is inherited from the AbstractList<E> class.


public boolean equals​(Object o)

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order.

Implementation Requirements: This implementation first checks if the specified object is this list. If so, it returns true; if not, it checks if the specified object is a list. If not, it returns false; if so, it iterates over both lists, comparing corresponding pairs of elements. If any comparison returns false, this method returns false. If either iterator runs out of elements before the other it returns false (as the lists are of unequal length); otherwise it returns true when the iterations complete.

Parameters: o - the object to be compared for equality with this list

Returns: true if the specified object is equal to this list

34/46

© Joanna Klukowska. CC-BY-SA.

equals method specification

The equals method in the ArrayList<E> class is inherited from the AbstractList<E> class.


public boolean equals​(Object o)

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order.

Implementation Requirements: This implementation first checks if the specified object is this list. If so, it returns true; if not, it checks if the specified object is a list. If not, it returns false; if so, it iterates over both lists, comparing corresponding pairs of elements. If any comparison returns false, this method returns false. If either iterator runs out of elements before the other it returns false (as the lists are of unequal length); otherwise it returns true when the iterations complete.

Parameters: o - the object to be compared for equality with this list

Returns: true if the specified object is equal to this list

Note that this specification allows us to use equals to compare different implementations of a list, for example an ArrayList instance with a LinkedList instance. The only requirement is that they contain the same elements in the same order.

35/46

© Joanna Klukowska. CC-BY-SA.

equals method implementation

This is the code from AbstractList<E> class.

public boolean equals(Object o) {
if (o == this) //check if the specified object is this list
return true;
if (!(o instanceof List)) //check if the specified object is a list
return false;
//iterate over both lists, comparing corresponding pairs of elements
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
36/46

© Joanna Klukowska. CC-BY-SA.

equals method alternative implementation

This is an alternative implementation of the same method.

public boolean equals(Object o) {
if (o == this) //check if the specified object is this list
return true;
if (!(o instanceof List)) //check if the specified object is a list
return false;
//check if lists are of the same length
if (this.size() != ((List<?>) o).size() ) {
return false;
}
//iterate over both lists, comparing corresponding pairs of elements
for (int i = 0; i < this.size(); i++ ) {
E o1 = this.get(i);
Object o2 = ((List<?>) o).get(i);
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return true;
}
37/46

© Joanna Klukowska. CC-BY-SA.

equals method alternative implementation

This is an alternative implementation of the same method.

public boolean equals(Object o) {
if (o == this) //check if the specified object is this list
return true;
if (!(o instanceof List)) //check if the specified object is a list
return false;
//check if lists are of the same length
if (this.size() != ((List<?>) o).size() ) {
return false;
}
//iterate over both lists, comparing corresponding pairs of elements
for (int i = 0; i < this.size(); i++ ) {
E o1 = this.get(i);
Object o2 = ((List<?>) o).get(i);
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return true;
}

What is the performance of this function?

38/46

ArrayList<E> Summary

39/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E>

  • Capacity of an ArrayList<E> is the size of the actual array used for storing data.
  • Size of an ArrayList<E> is the number of elements that have been stored in it.

CapacitySize

  • An empty list will often have a non-zero capacity.
  • Capacity is not decreased as elements are removed (could be, but the ArrayList<E> class does not do it). But it can be reduced by an explicit call to trimToSize()
40/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E>

  • Capacity of an ArrayList<E> is the size of the actual array used for storing data.
  • Size of an ArrayList<E> is the number of elements that have been stored in it.

CapacitySize

  • An empty list will often have a non-zero capacity.
  • Capacity is not decreased as elements are removed (could be, but the ArrayList<E> class does not do it). But it can be reduced by an explicit call to trimToSize()

Amortized Performance

front back index in the middle
adding O(N) O(1) O(N)
removing O(N) O(1) O(N)
41/46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E>

  • Capacity of an ArrayList<E> is the size of the actual array used for storing data.
  • Size of an ArrayList<E> is the number of elements that have been stored in it.

CapacitySize

  • An empty list will often have a non-zero capacity.
  • Capacity is not decreased as elements are removed (could be, but the ArrayList<E> class does not do it). But it can be reduced by an explicit call to trimToSize()

Amortized Performance

front back index in the middle
adding O(N) O(1) O(N)
removing O(N) O(1) O(N)

Performance for other functions

function performance
get(index) O(1)
set(index,element) O(1)
size() O(1)
isEmpty() O(1)
clear() O(N) resets each value to null
function performance
contains(object) O(N)
indexOf(object) O(N)
equals() O(N)
toArray() O(N)
sort() O(N logN)
42/46

Examples and Things to Think About

43/46

© Joanna Klukowska. CC-BY-SA.

Implementing contains and indexOf

Without looking at the source code of the ArrayList<E> class, implement the contains and indexOf methods.

Use the documentation to find out exactly what they are supposed to do.

Once you have your code, compare it to the one in the ArrayList<E> class and see how similar/different it is.

  • Is it equivalent (i.e., does your do the same thing that the ArrayList<E> code does )?
  • Does it have the same performance?
44/46

© Joanna Klukowska. CC-BY-SA.

Binary search on an ArrayList<E>

The contains() and indexOf methods provides a linear search for the ArrayList<E>. But we know that a binary search is much more efficient if the data is sorted.

  • Write a method boolean isSorted (ArrayList<E> list ) that determines if the given list is sorted or not.

  • Write a method int binSearch(ArrayList<E> list, E key) that performs the binary search and returns the index of key or -1 if the key is not present in the list.

  • Write a method int bestSearch(ArrayList<E> list, E key) that first determines if the list is sorted or not, and selects to either use binSearch or indexOf method accordingly. What is the performance of this method? Is it better, worse or the same as the performance of the indexOf method?

45/46

© Joanna Klukowska. CC-BY-SA.

Binary search on an ArrayList<E>

The contains() and indexOf methods provides a linear search for the ArrayList<E>. But we know that a binary search is much more efficient if the data is sorted.

  • Write a method boolean isSorted (ArrayList<E> list ) that determines if the given list is sorted or not.

  • Write a method int binSearch(ArrayList<E> list, E key) that performs the binary search and returns the index of key or -1 if the key is not present in the list.

  • Write a method int bestSearch(ArrayList<E> list, E key) that first determines if the list is sorted or not, and selects to either use binSearch or indexOf method accordingly. What is the performance of this method? Is it better, worse or the same as the performance of the indexOf method?

Note that the above methods are NOT part of the ArrayList<E> class. They should be implemented as stand alone methods.

46/46

CSCI-UA 102

Data Structures


Lists (Part 2)

Instructor: Joanna Klukowska


Copyright 2020 Joanna Klukowska. Unless noted otherwise all content is released under a
Creative Commons Attribution-ShareAlike 4.0 International License.
Background image by Stewart Weiss

1 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

2 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
3 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
  • ArrayList<E> = an array (that can store any type of data) + operations that make everything easy
4 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
  • ArrayList<E> = an array (that can store any type of data) + operations that make everything easy

Where did the pirate store his loot?

  • In an ArrayList, because ArrayLists can grow!

Dana L., 2018

5 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

  • What makes this approach different from a linked lists approach?

    • the data is stored in an array instead of in nodes
  • ArrayList<E> = an array (that can store any type of data) + operations that make everything easy

Where did the pirate store his loot?

  • In an ArrayList, because ArrayLists can grow!

Dana L., 2018

  • So how do they grow? And can we make it efficient?
6 / 46

Growing the Array

7 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

8 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

9 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

growing an array

  • creating a new array and copying the data is fairly easy, but how much more room should we allocate?
    • if it is too large, then we have an array that is much too big for our data
    • if it is too little, then we might need to do this again, and again, and again
10 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
11 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
  • where does the O(N) time come from in the above function
12 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
  • where does the O(N) time come from in the above function
    • arraycopy() is linear, it runs in time proportional to the size of the array
    • but it is in practice faster than executing:
      for (int i = 0; i < size; i++)
      bigger[i] = array[i];
13 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array

Adding to the end of an array is O(1), except for when we need to add to a completely full array when it is O(N).

public boolean add(E element) {
if (size >= array.length) {
// make a bigger array and copy over the elements
E[] bigger = (E[]) new Object[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
array = bigger;
}
array[size] = element;
size++;
return true;
}
  • where does the O(N) time come from in the above function
    • arraycopy() is linear, it runs in time proportional to the size of the array
    • but it is in practice faster than executing:
      for (int i = 0; i < size; i++)
      bigger[i] = array[i];
  • so what is the performance of this function overall (not just sometimes O(1) and sometimes O(N))?
14 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
15 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
16 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
17 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
18 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)
19 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

20 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

21 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

The add at the end (append) operation for an ArrayList has an amortized time performance of O(1) as long as the array size in increased by a multiplicative factor, not an additive factor.

22 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

The add at the end (append) operation for an ArrayList has an amortized time performance of O(1) as long as the array size in increased by a multiplicative factor, not an additive factor.

  • this means that we need to multiply the current size of an array by a constant to get the size of the bigger array: if N starts as 10, and the multiplicative constant is 2, the sizes will be: 10, 20, 40, 80, 160, ...
23 / 46

© Joanna Klukowska. CC-BY-SA.

Growing the Array - Amortized Analysis

If the initial size of the array is N, then

  • adding first element takes O(1)
  • adding second element takes O(1)
  • ...
  • adding Nth element takes O(1)
  • adding N+1st element takes O(N)

Intuitively, we have N * O(1) + 1 * O(N) to add N+1 elements -> this is still a constant time for each operation (almost).

In amortized analysis we are interested in the performance of a given operation on average - when we repeat the operation many time and calculate how much time it took divided by the number of repeats. (See Amortized Analysis on Wikipedia for more formal details.)

The add at the end (append) operation for an ArrayList has an amortized time performance of O(1) as long as the array size in increased by a multiplicative factor, not an additive factor.

  • this means that we need to multiply the current size of an array by a constant to get the size of the bigger array: if N starts as 10, and the multiplicative constant is 2, the sizes will be: 10, 20, 40, 80, 160, ...

  • not add some constant to the current size: if N starts at 10, and the additive constant is 2, the sizes would be: 10, 12, 14, 16, 18, ...

24 / 46

Adding and Removing

25 / 46

© Joanna Klukowska. CC-BY-SA.

Adding at an Arbitrary Position

If we want to add an element to an arbitrary position in the list, we will need to shift the values that are there to the higher indexes to make room for it.

26 / 46

© Joanna Klukowska. CC-BY-SA.

Adding at an Arbitrary Position

If we want to add an element to an arbitrary position in the list, we will need to shift the values that are there to the higher indexes to make room for it.

27 / 46

© Joanna Klukowska. CC-BY-SA.

Adding at an Arbitrary Position

If we want to add an element to an arbitrary position in the list, we will need to shift the values that are there to the higher indexes to make room for it.

growing an array

This operation is O(N) because we need to shift the elements by one position to the right (higher indexes).

28 / 46

© Joanna Klukowska. CC-BY-SA.

Removing from an Arbitrary Position

If we want to remove an element from an arbitrary position in the list, we will need to shift the values in the higher indexes to fill in the whole that remains after removing an element.

29 / 46

© Joanna Klukowska. CC-BY-SA.

Removing from an Arbitrary Position

If we want to remove an element from an arbitrary position in the list, we will need to shift the values in the higher indexes to fill in the whole that remains after removing an element.

30 / 46

© Joanna Klukowska. CC-BY-SA.

Removing from an Arbitrary Position

If we want to remove an element from an arbitrary position in the list, we will need to shift the values in the higher indexes to fill in the whole that remains after removing an element.

growing an array

This operation is O(N) because we need to shift the elements by one position to the left (lower indexes).

31 / 46

Comparing ArrayList<E> Objects for Equality

32 / 46

© Joanna Klukowska. CC-BY-SA.

equals method specification

The equals method in the ArrayList<E> class is inherited from the AbstractList<E> class.


33 / 46

© Joanna Klukowska. CC-BY-SA.

equals method specification

The equals method in the ArrayList<E> class is inherited from the AbstractList<E> class.


public boolean equals​(Object o)

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order.

Implementation Requirements: This implementation first checks if the specified object is this list. If so, it returns true; if not, it checks if the specified object is a list. If not, it returns false; if so, it iterates over both lists, comparing corresponding pairs of elements. If any comparison returns false, this method returns false. If either iterator runs out of elements before the other it returns false (as the lists are of unequal length); otherwise it returns true when the iterations complete.

Parameters: o - the object to be compared for equality with this list

Returns: true if the specified object is equal to this list

34 / 46

© Joanna Klukowska. CC-BY-SA.

equals method specification

The equals method in the ArrayList<E> class is inherited from the AbstractList<E> class.


public boolean equals​(Object o)

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order.

Implementation Requirements: This implementation first checks if the specified object is this list. If so, it returns true; if not, it checks if the specified object is a list. If not, it returns false; if so, it iterates over both lists, comparing corresponding pairs of elements. If any comparison returns false, this method returns false. If either iterator runs out of elements before the other it returns false (as the lists are of unequal length); otherwise it returns true when the iterations complete.

Parameters: o - the object to be compared for equality with this list

Returns: true if the specified object is equal to this list

Note that this specification allows us to use equals to compare different implementations of a list, for example an ArrayList instance with a LinkedList instance. The only requirement is that they contain the same elements in the same order.

35 / 46

© Joanna Klukowska. CC-BY-SA.

equals method implementation

This is the code from AbstractList<E> class.

public boolean equals(Object o) {
if (o == this) //check if the specified object is this list
return true;
if (!(o instanceof List)) //check if the specified object is a list
return false;
//iterate over both lists, comparing corresponding pairs of elements
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
36 / 46

© Joanna Klukowska. CC-BY-SA.

equals method alternative implementation

This is an alternative implementation of the same method.

public boolean equals(Object o) {
if (o == this) //check if the specified object is this list
return true;
if (!(o instanceof List)) //check if the specified object is a list
return false;
//check if lists are of the same length
if (this.size() != ((List<?>) o).size() ) {
return false;
}
//iterate over both lists, comparing corresponding pairs of elements
for (int i = 0; i < this.size(); i++ ) {
E o1 = this.get(i);
Object o2 = ((List<?>) o).get(i);
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return true;
}
37 / 46

© Joanna Klukowska. CC-BY-SA.

equals method alternative implementation

This is an alternative implementation of the same method.

public boolean equals(Object o) {
if (o == this) //check if the specified object is this list
return true;
if (!(o instanceof List)) //check if the specified object is a list
return false;
//check if lists are of the same length
if (this.size() != ((List<?>) o).size() ) {
return false;
}
//iterate over both lists, comparing corresponding pairs of elements
for (int i = 0; i < this.size(); i++ ) {
E o1 = this.get(i);
Object o2 = ((List<?>) o).get(i);
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return true;
}

What is the performance of this function?

38 / 46

ArrayList<E> Summary

39 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E>

  • Capacity of an ArrayList<E> is the size of the actual array used for storing data.
  • Size of an ArrayList<E> is the number of elements that have been stored in it.

CapacitySize

  • An empty list will often have a non-zero capacity.
  • Capacity is not decreased as elements are removed (could be, but the ArrayList<E> class does not do it). But it can be reduced by an explicit call to trimToSize()
40 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E>

  • Capacity of an ArrayList<E> is the size of the actual array used for storing data.
  • Size of an ArrayList<E> is the number of elements that have been stored in it.

CapacitySize

  • An empty list will often have a non-zero capacity.
  • Capacity is not decreased as elements are removed (could be, but the ArrayList<E> class does not do it). But it can be reduced by an explicit call to trimToSize()

Amortized Performance

front back index in the middle
adding O(N) O(1) O(N)
removing O(N) O(1) O(N)
41 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E>

  • Capacity of an ArrayList<E> is the size of the actual array used for storing data.
  • Size of an ArrayList<E> is the number of elements that have been stored in it.

CapacitySize

  • An empty list will often have a non-zero capacity.
  • Capacity is not decreased as elements are removed (could be, but the ArrayList<E> class does not do it). But it can be reduced by an explicit call to trimToSize()

Amortized Performance

front back index in the middle
adding O(N) O(1) O(N)
removing O(N) O(1) O(N)

Performance for other functions

function performance
get(index) O(1)
set(index,element) O(1)
size() O(1)
isEmpty() O(1)
clear() O(N) resets each value to null
function performance
contains(object) O(N)
indexOf(object) O(N)
equals() O(N)
toArray() O(N)
sort() O(N logN)
42 / 46

Examples and Things to Think About

43 / 46

© Joanna Klukowska. CC-BY-SA.

Implementing contains and indexOf

Without looking at the source code of the ArrayList<E> class, implement the contains and indexOf methods.

Use the documentation to find out exactly what they are supposed to do.

Once you have your code, compare it to the one in the ArrayList<E> class and see how similar/different it is.

  • Is it equivalent (i.e., does your do the same thing that the ArrayList<E> code does )?
  • Does it have the same performance?
44 / 46

© Joanna Klukowska. CC-BY-SA.

Binary search on an ArrayList<E>

The contains() and indexOf methods provides a linear search for the ArrayList<E>. But we know that a binary search is much more efficient if the data is sorted.

  • Write a method boolean isSorted (ArrayList<E> list ) that determines if the given list is sorted or not.

  • Write a method int binSearch(ArrayList<E> list, E key) that performs the binary search and returns the index of key or -1 if the key is not present in the list.

  • Write a method int bestSearch(ArrayList<E> list, E key) that first determines if the list is sorted or not, and selects to either use binSearch or indexOf method accordingly. What is the performance of this method? Is it better, worse or the same as the performance of the indexOf method?

45 / 46

© Joanna Klukowska. CC-BY-SA.

Binary search on an ArrayList<E>

The contains() and indexOf methods provides a linear search for the ArrayList<E>. But we know that a binary search is much more efficient if the data is sorted.

  • Write a method boolean isSorted (ArrayList<E> list ) that determines if the given list is sorted or not.

  • Write a method int binSearch(ArrayList<E> list, E key) that performs the binary search and returns the index of key or -1 if the key is not present in the list.

  • Write a method int bestSearch(ArrayList<E> list, E key) that first determines if the list is sorted or not, and selects to either use binSearch or indexOf method accordingly. What is the performance of this method? Is it better, worse or the same as the performance of the indexOf method?

Note that the above methods are NOT part of the ArrayList<E> class. They should be implemented as stand alone methods.

46 / 46

© Joanna Klukowska. CC-BY-SA.

ArrayList<E> as an Example of Array Based Implementation of a List

Instead of creating our own implementation that is based on an array, we will look at the existing one implemented in Java: ArrayList<E> class.

2 / 46
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow
+ -
Notes for current slide
Notes for next slide
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow