Advanced Programming
SUT • Spring 2019
Containers
Collection
Set
Map
LinkedList
Iterator
Suppose we have an array of students
Student[] students = new Student[34];
What if we do not know the array size?
A default initial size
What if we want to add more students to array?
Double the size of array
Copy old elements
What if we want to remove some students from array?
Nullify the element & shift the others
We need a dynamic array
Student[] students = new Student[0];
students.add(new Student("Ali Alavi"));
students.add(new Student("Taghi Taghavi"));
System.out.println(students[1]);
students.remove(0);
// But arrays are not so cute!
Java introduces Collection classes for this purpose
ArrayList students = new ArrayList();
students.add(new Student("Ali Alavi"));
students.add(new Student("Taghi Taghavi"));
students.remove(0);
ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("Ali Alavi"));
students.add(new Student("Taghi Taghavi"));
students.remove(0);
students.remove(new Student("Ali Alavi"));
Student student = students.get(0);
System.out.println(student);
// ArrayList<T> implements generic interface List<T>
interface List<E>{
int size();
boolean isEmpty();
boolean contains(Object o);
boolean add(E e);
boolean remove(Object o);
void clear();
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
List<E> subList(int fromIndex, int toIndex);
}
ArrayList<String> list = new ArrayList<String>();
Scanner scanner = new Scanner(System.in);
while(true){
String input = scanner.next();
if(input.equalsIgnoreCase("exit"))
break;
list.add(input);
}
if(list.isEmpty()){
System.out.println("No string entered");
}else{
System.out.println("" + list.size() +
" strings enetered");
if(list.contains("Ali"))
System.out.println("Ali Found!");
for (String s : list) {
System.out.println(s);
}
}
Do we need a dynamic array?
Add
Remove
Performance issue
ArrayList is implemented using an array
Guess how?
String[] strings = {"ali", "taghi"};
ArrayList<String> list =
new ArrayList<String>();
for (String string : strings) {
list.add(string);
}
Two methods:
Object[] toArray();
<T> T[] toArray(T[] a);
ArrayList<String> list = new ArrayList<String>();
Object[] array = list.toArray();
String[] array2 = list.toArray(new String[list.size()]);
ArrayList<String> as;
ArrayList<Object> ao;
List<Object> lo;
List<String> ls;
True/False?
ArrayList<String> is subclass of List<String>
ls = as;
ArrayList<String> is subclass of ArrayList<Object>
ao = as;
ArrayList<String> is subclass of List<Object>
lo = as;
In the heart of an ArrayList, an array lives…
public class ArrayList<E> ... ,implements List<E>,...{
private Object[] elementData;
private int size;
public boolean add(E e) {
ensureCapacity(size + 1);
elementData[size++] = e;
return true;
}
}
Why toArray() returns Object[]?
public interface Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
boolean add(E e);
boolean remove(Object o);
void clear();
Object[] toArray();
<T> T[] toArray(T[] a);
}
LinkedList and ArrayList are both subclass of List
ArrayList is implemented by an array
LinkedList is implemented by a doubly linked list
It is used like an ArrayList
Because they are brothers! (subclass of List)
List<String> list = new LinkedList<String>();
list.add("Ali");
list.add("Taghi");
System.out.println(list.get(0));
list.remove("Taghi");
for (String string : list) {
System.out.println(string);
}
LinkedList stores two links for each element
if you want to do many insertions and removals in the middle of a list
a LinkedList is better
If not, an ArrayList is typically faster
long start = System.currentTimeMillis();
doSomthing();
long end = System.currentTimeMillis();
System.err.println(end - start);
{1,2,3,1,4,2} = {4,3,2,1}
set.add(1)
set.add(2)
set.add(3)
set.add(1)
set.remove(1)
Set == {3,2}
A set is a list with no duplicate
Suppose we want to implement such a class
How?!
class Set<E> extends ArrayList<E>{
public boolean add(E e) {
if(!contains(e))
return super.add(e);
return false;
}
public boolean add(int index, E e) {...}
}
When set.add(value) is invoked
It checks whether there is any element equal to value
If any equal element found, add will return
We should implement appropriate equals() method
equals() is invoked implicitly
Set is an interface
public interface Set<E> extends Collection<E>
HashSet is one of its (popular) implementations
Set and HashSet are generic classes
public class HashSet<E> implements Set<E>
Set<String> set= new HashSet<String>();
set.add("Ali");
set.add("Taghi");
set.add("Ali");
for (String string : set) {
System.out.println(string);
}
Set<Student> set= new HashSet<Student>();
set.add(new Student("Ali"));
set.add(new Student("Taghi"));
set.add(new Student("Ali"));
set.remove(new Student("Taghi"));
for (Student student : set) {
System.out.println(student);
}
List provides access via an index
Set does not
List is ordered
Set checks for duplicates
List is (usually) better in performance
Set may be better in memory consumption
Should we allow duplicates?
If not, use sets
HashSet is not implemented by a List
Map is not a collection
Map is a table
public interface Map<K,V>
Map<K, V> is something like a List<Pair<K,V>>
First element of each pair is called the key
Second element of each pair is called the value
Duplicate for keys is not allowed
Duplicate for values is possible
Command | Keys | Values |
---|---|---|
map.put(87300876, “Ali Alavi”) | 87300876 | Ali Alavi |
map.put(87234431, “Taghi Taghavi”) | 87300876 | Ali Alavi |
87234431 | Taghi Taghavi | |
map.put(87300876, “Naghi Naghavi”) | 87300876 | Naghi Naghavi |
87234431 | Taghi Taghavi |
public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
}
}
Map is an interface
public interface Map<K,V>
HashMap is one of its (popular) implementations
public class HashMap<K,V> implements Map<K,V>
Map<Integer, String> map = new HashMap<>();
map.put(87300876, "Ali Alavi");
map.put(87234431, "Taghi Taghavi");
map.put(87300876, "Naghi Naghavi");
String name = map.get(87300876);
System.out.println(name);
Map<Student, Double> map = new HashMap<>();
map.put(new Student("Ali Alavi"), new Double(18.76));
map.put(new Student("Taghi Taghavi"), new Double(15.43));
map.put(new Student("Naghi Naghavi"), new Double(17.26));
map.put(new Student("Naghi Naghavi"), new Double(15.26));
map.remove(new Student("Naghi Naghavi"));
Double average = map.get(new Student("Taghi Taghavi"));
System.out.println("Avg of Taghi=" + average);
for(Student student : map.keySet()){
System.out.println(student.toString());
}
Double totalSum = 0.0;
for(Double avg : map.values()){
totalSum += avg;
}
System.out.println("Total Average = " + (totalSum/map.size()));
Iterator is a mechanism for walking on elements of a collection
Before foreach (before Java5) it was the only mechanism
iterator() is declared in Iterable interface
In fact for-each is applicable on any Iterable object
public interface Iterable<T> {
Iterator<T> iterator();
}
public interface Collection<E> extends Iterable<E> {…}
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(4);
arrayList.add(5);
for (Integer next : arrayList) {
System.out.println(next);
}
Iterator<Integer> iterator = arrayList.iterator();
while(iterator.hasNext()){
Integer next = iterator.next();
System.out.println(next);
}
Suppose some processes are modifying the same collection
Java containers have a mechanism to prevent it
Suppose you’re in the middle of iterating through a container
And then some other process steps in and changes an object in that container
Insert, remove, …
there are many scenarios for disaster.
Maybe you’ve already passed that element in the container
Maybe it’s ahead of you
Maybe the size of the container shrinks after you call size( )
If a collection is modified by one of its methods after an iterator is created for that collection
The iterator immediately becomes invalid
Any operations performed with the iterator after this point throw ConcurrentModificationExceptions
For this reason, iterators are said to be “fail fast”
public class FailFast {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
Iterator<String> it = c.iterator();
c.add("An object");
String s = it.next(); // Exception line
}
}
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
for (Integer integer: list) // Exception line
if(integer.equals(2))
list.remove(integer);
Long[] array = new Long[100];
Arrays.fill(array, 5);
Long[] copy = Arrays.copyOf(array, 200);
//An unmodifiable list:
List<Integer> asList = Arrays.asList(1, 2 , 3, 4);
List<Long> asList2 = Arrays.asList(array);
Arrays.sort(array);
Write the method
removeAlis(List<String> names)
It takes a List<String> as parameter
Removes all the elements which start with “Ali”
static void removeAli(List<String> list){
for (String string : list)
if(string.startsWith("Ali"))
list.remove(string);
}
public static void removeAli(ArrayList<String> list){
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String string = iterator.next();
if(string.startsWith("Ali"))
iterator.remove();
}
}
public static void removeAli(ArrayList<String> list){
for (Iterator<String> iterator = list.iterator();
iterator.hasNext(); ) {
String string = iterator.next();
if(string.startsWith("Ali"))
iterator.remove();
}
}
public static void removeAli(ArrayList<String> list){
for (int i = list.size()-1; i >= 0; i--)
if(list.get(i).startsWith("Ali"))
list.remove(i);
}
hashCode() is one of Object methods
like equals, toString and finalize
It creates a hash from the object
Used in classes like HashMap and HashSet for faster retrieval