Collections : Lists : ArrayList

Lists are collections that maintain their elements in order and can contain duplicates. The elements in a list are ordered. Each element, therefore, has a position in the list. Main implementations of the List interface are provided in the java.util package:
ArrayList, LinkedList, Vector, Stack etc. Lets have an ArrayList of class Person which have name, age as attributes initialised from constructor.


List<Person> personList=new ArrayList<Person>();
personList.add(new Person("Tom", 22));
personList.add(new Person("Jake", 32));
personList.add(new Person("Stan", 25));
personList.add(new Person("John", 27));

Some basic operations:

  • add(E element)- append the specified element to the end of the list.
  • E get(int index)- returns the element at the specified index.
  • E set(int index, E element) – Replaces the element at the specified index with the specified element. It returns the previous
    element at the specified index.
  • void add(int index, E element)- inserts the specified element at the specified index. If necessary, it shifts the element previously at this index and any subsequent elements one position toward the end of the list.
  • E remove(int index) – Deletes and returns the element at the specified index, contracting the list accordingly.

Lets see this by example :


System.out.println("List count=>"+personList.size());
System.out.println("Get index 0=>"+personList.get(0));
System.out.println("Replace index 2=>"+personList.set(2, new Person("Ryan", 26)));//previous object will be returned
System.out.println("List count=>"+personList.size());
personList.add(2, new Person("Sam", 30));//the object here will be added and other element will be shifted right
System.out.println("List count=>"+personList.size());
System.out.println("Get index 2=>"+personList.get(2));
System.out.println("Get index 3=>"+personList.get(3));
System.out.println("Removing at index 2=>"+personList.remove(2));//deletes and return the element
System.out.println("List count=>"+personList.size());

Output :

List count=>4
Get index 0=>Name=Tom    Age=22
Replace index 2=>Name=Stan    Age=25
List count=>4
List count=>5
Get index 2=>Name=Sam    Age=30
Get index 3=>Name=Ryan    Age=26
Removing at index 2=>Name=Sam    Age=30
List count=>4

Iterator is used to traverse through over collection objects like list,set etc. It basically goes over each element in the collection and optionally can perform certain tasks like removing, updating objects in collections. Iterator in Java was introduced form JDK 1.4 and it provides alternative to Enumeration, which is obsolete now days. Iterator is different to Enumeration in two main ways:

  • Iterator allows programmer to remove elements from Collection during iteration.
  • Names are shortened.

Iterator methods:

  • hasNext()
  • next()
  • remove()

Lets iterate over the previously created personList.


Iterator&lt;Person&gt; myInterator=personList.iterator();
while(myInterator.hasNext()){
System.out.println(myInterator.next());
}

Output:


Name=Tom    Age=22
Name=Jake    Age=32
Name=Stan    Age=25
Name=John    Age=27

We cannot use for-loop to modify or remove from a Collection. It will throw java.util.ConcurrentModificationException.


for (Person person : personList) {
System.out.println(person);
if(person.getAge()==27){
personList.remove(person);
}

}

Output:


Exception in thread &quot;main&quot; java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)

Using Iterator:


Iterator<Person> myInterator=personList.iterator();
while(myInterator.hasNext()){
Person p=myInterator.next();
System.out.println(p);
if(p.getAge()==32){
myInterator.remove();
}
if(p.getAge()==22){
p.setAge(24);
}
}
System.out.println("After removing/updating");
myInterator=personList.iterator();
while(myInterator.hasNext()){
System.out.println(myInterator.next());
}

Output:


Name=Tom    Age=22
Name=Jake    Age=32
Name=Stan    Age=25
Name=John    Age=27
After removing/updating
Name=Tom    Age=24
Name=Stan    Age=25
Name=John    Age=27

Q-Why are Iterators returned by ArrayList called Fail Fast ?
A-Because, if list is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Q-Consider a scenario. If an ArrayList has to be iterate to read data only, what are the possible ways and which is the fastest?
A- It can be done in two ways, using for loop or using iterator of ArrayList. The first option is faster than using iterator. Because value stored in arraylist is indexed access. So while accessing the value is accessed directly as per the index.

Q- Now another question with respect to above question is if accessing through iterator is slow then why do we need it and when to use it.
A-For loop does not allow the updation in the array(add or remove operation) inside the loop whereas Iterator does. Also Iterator can be used where there is no clue what type of collections will be used because all collections have iterator. Iterator follows Iterator design pattern. Iterator Pattern is a type of behavioral pattern. The Iterator pattern is one, which allows you to navigate through a collection of data using a common interface without knowing about the underlying implementation. Iterator should be implemented as an interface. This allows the user to implement it anyway its easier for him/her to return data.

The ListIterator interface is a bidirectional iterator for lists.

ListIterator<E> listIterator()
ListIterator<E> listIterator(int index)
The iterator from the first method traverses the elements consecutively, starting with the first element of the list, whereas the iterator from the second method starts traversing the list from the element indicated by the specified index.
It extends the Iterator interface and allows the list to be traversed in either direction. When traversing lists, it can be helpful to
imagine a cursor moving forward or backward between the elements when calls are made to the next() and the previous() methods, respectively. The element that the cursor passes over is returned. When the remove() method is called, the element last passed over is removed from the list.
ListIterator methods – add(E e)-The new element is inserted before the implicit cursor: a subsequent call to next would be unaffected, and a subsequent call to previous would return the new element; hasNex(); hasPrevious() ;next(); nextIndex(); previous() ; previousIndex() ; remove() ; set(E e)-Replaces the last element returned by next or previous with the;specified element.

Using polymorphic arguments and generics

Lets have the following classes:


public abstract class Animal {
void eat(){
System.out.println("Animal Eating");
}
}
public class Dog extends Animal{
void bark(){
System.out.println("Dog barks");
}
}
public class Cat extends Animal {
void meow() {
System.out.println("Cat meows");
}
}

Now in our test class lets have following methods:


public static void testOnArrays() {
Animal[] animals = { new Dog(), new Cat()};
Dog[] dogs = { new Dog(), new Dog()};
System.out.println("Pass Animals Array");
methodTakesArrayOfAnimals(animals);
System.out.println("Pass Dogs Array");
methodTakesArrayOfAnimals(dogs);

}
public static void methodTakesArrayOfAnimals(Animal[] animals) {
for (Animal animal : animals) {
animal.eat();
}
}

Output:


Pass Animals Array
Animal Eating
Animal Eating
Pass Dogs Array
Animal Eating
Animal Eating

Point to be noted – We can pass the child array to a method that takes parent array.

Lets try the same thing with ArrayList:


public static void testOnArraLists() {
ArrayList<Animal> animals = new ArrayList<Animal>();
System.out.println("Pass Animals ArrayList");
animals.add(new Dog());
animals.add(new Cat());
methodTakesArrayListOfAnimals(animals);
ArrayList<Dog> dogs = new ArrayList<Dog>();
System.out.println("Pass Animals ArrayList");
dogs.add(new Dog());
dogs.add(new Dog());
methodTakesArrayListOfAnimals(dogs); //Compile time array
}
public static void methodTakesArrayListOfAnimals(ArrayList<Animal> animals) {
for (Animal animal : animals) {
animal.eat();
}
}

Here we will get a compile time error saying that dog arraylist cannot be passed as the method takes Animal arraylist. If you declare a method to take ArrayList<Animal> it can take ONLY an ArrayList<Animal>, not ArrayList<Dog> or ArrayList<Cat>.

Imagine the compiler let you get away with that. It let you pass an ArrayList<Dog> to a method, then you may add a cat object to it inside like:
public void takeAnimals(ArrayList<Animal> animals) {
animals.add(new Cat());
}
So, if this is why they won’t let you pass a Dog ArrayList into a method that takes an Animal ArrayList—to stop you from possibly putting a Cat in what was actually a Dog list.

Q- then why does it work with arrays? Don’t you have the same problem with arrays? Can’t you still add a Cat object to a Dog[] ?

Array types are checked again at runtime, but collection type checks happen only when you compile.

public void takeAnimals(Animal[] animals) {
animals[0] = new Cat();
}

It compiles, but when we run :
Exception in thread “main” java.lang.ArrayStoreException: Cat

Q-So, how to get a way to pass dog list?
A-Wildcards

It looks unusual, but there is a way to create a method argument that can accept an ArrayList of any Animal subtype. The simplest way is to use a wildcard—added to the Java language explicitly for this reason.


public static void methodTakesArrayListOfAnimalsWildCard(ArrayList<? extends Animal> animals) {
for (Animal animal : animals) {
animal.eat();
}
}

Remember, the keyword “extends” here means either extends OR implements depending on the type. So if you want to take an ArrayList of types that implement the Pet interface, you’d declare it as: ArrayList<? extends Pet>.
When you use a wildcard in your method argument, the compiler will STOP you from doing anything that could hurt the list referenced by the method parameter. You can still invoke methods on the elements in the list, but you cannot add elements to the list. In other words, you can do things with the list elements, but you can’t put new things in the list. So you’re safe at runtime, because the compiler won’t let you do anything that might be horrible at runtime.

So, animal.eat() is ok.
But this will not compile: animals.add(new Cat());

Alternate syntax for doing the same thing:

public <T extends Animal> void takeThing(ArrayList<T> list)

is same as:

public void takeThing(ArrayList<? extends Animal> list)

Q-If they both do the same thing, why would you use one over the other?

public <T extends Animal> void takeThing(ArrayList<T> one, ArrayList<T> two)

instead of typing

public void takeThing(ArrayList<? extends Animal> one, ArrayList<? extends Animal> two)

Grab all the code from my GitHub repository.

1 Comment (+add yours?)

  1. Amrenedra
    Apr 13, 2015 @ 09:53:10

    Very Good Doc DJ.

    Amrendra

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: