Collections in Java

The collections framework in java.util provides a number of generic classes for sets of data with functionality that can’t be provided by regular arrays.

Collections framework contains interfaces for Collection, with main sub-interfaces List and Set, and mapping collection Map. Collections are the root interface and are being implemented by many other collection frameworks.

Removing items from a List within a loop

It is tricky to remove items from a list while within a loop, this is due to the fact that the index and length of the list gets changed.

Given the following list, here are some examples that will give an unexpected result and some that will give the correct result.

List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Strawberry");

INCORRECT
Removing in iteration of for statement Skips “Banana”:

The code sample will only print Apple and Strawberry. Banana is skipped because it moves to index 0 once Apple is deleted, but at the same time i gets incremented to 1.

for (int i = 0; i < fruits.size(); i++) {
     System.out.println (fruits.get(i));
     if ("Apple".equals(fruits.get(i))) {
        fruits.remove(i);
     }
}

Removing in the enhanced for statement Throws Exception:

Because of iterating over collection and modifying it at the same time.

Throws: java.util.ConcurrentModificationException

for (String fruit : fruits) {
     System.out.println(fruit);
     if ("Apple".equals(fruit)) {
        fruits.remove(fruit);
     }
}

CORRECT
Removing in while loop using an Iterator

Iterator fruitIterator = fruits.iterator();
while(fruitIterator.hasNext()) {
    String fruit = fruitIterator.next();
    System.out.println(fruit);
    if ("Apple".equals(fruit)) {
         fruitIterator.remove();
     }
}

The Iterator interface has a remove() method built in just for this case. However, this method is marked as “optional” in the documentation, and it might throw an UnsupportedOperationException.

Throws: UnsupportedOperationException – if the remove operation is not supported by this iterator

Therefore, it is advisable to check the documentation to make sure this operation is supported (in practice, unless the collection is an immutable one obtained through a 3rd party library or the use of one of the Collections.unmodifiable…() method, the operation is almost always supported).

While using an Iterator a ConcurrentModificationException is thrown when the modCount of the List is changed from when the Iterator was created. This could have happened in the same thread or in a multi-threaded application sharing the same list.

Related Article: Immutable Empty Collections and iterators in Java with Examples

A modCount is an int variable which counts the number of times this list has been structurally modified. A structural change essentially means an add() or remove() operation being invoked on Collection object (changes made by Iterator are not counted). When the Iterator is created, it stores this modCount and on every iteration of the List checks if the current modCount is same as and when the Iterator was created. If there is a change in the modCount value it throws a ConcurrentModificationException.

Hence for the above-declared list, an operation like below will not throw any exception:

Iterator fruitIterator = fruits.iterator();
fruits.set(0, "Watermelon");
while(fruitIterator.hasNext()){
     System.out.println(fruitIterator.next());
}

But adding a new element to the List after initializing an Iterator will throw a ConcurrentModificationException:

Iterator fruitIterator = fruits.iterator();
fruits.add("Watermelon");
while(fruitIterator.hasNext()){
    System.out.println(fruitIterator.next()); //ConcurrentModificationException here
}
Iterating backwards
for (int i = (fruits.size() - 1); i >=0; i--) {
     System.out.println (fruits.get(i));
     if ("Apple".equals(fruits.get(i))) {
          fruits.remove(i);
     }
}

This does not skip anything. The downside of this approach is that the output is reverse. However, in most cases where you remove items that will not matter. You should never do this with LinkedList.

Iterating forward, adjusting the loop index
    for (int i = 0; i < fruits.size(); i++) {
    System.out.println (fruits.get(i));
    if ("Apple".equals(fruits.get(i))) {
         fruits.remove(i);
         i--;
    }
}

This does not skip anything. When the ith element is removed from the List, the element originally positioned at index i+1 becomes the new ith element. Therefore, the loop can decrement i in order for the next iteration to process the next element, without skipping.

Using a "should-be-removed" list
ArrayList shouldBeRemoved = new ArrayList();
for (String str : currentArrayList) {
     if (condition) {
        shouldBeRemoved.add(str);
     }
}
currentArrayList.removeAll(shouldBeRemoved);

This solution enables the developer to check if the correct elements are removed in a cleaner way.

Version ≥ Java SE 8
In Java 8 the following alternatives are possible. These are cleaner and more straight forward if the removing does not have to happen in a loop.

Filtering a Stream

A List can be streamed and filtered. A proper filter can be used to remove all undesired elements.

List filteredList =
     fruits.stream().filter(p -> !"Apple".equals(p)).collect(Collectors.toList());

Note that unlike all the other examples here, this example produces a new List instance and keeps the original List unchanged.

Using removeIf

Saves the overhead of constructing a stream if all that is needed is to remove a set of items.

fruits.removeIf(p -> "Apple".equals(p));

Constructing collections from existing data

Standard Collections
Java Collections framework

A simple way to construct a List from individual data values is to use java.utils.Arrays method Arrays.asList:

List data = Arrays.asList("ab", "bc", "cd", "ab", "bc", "cd");

All standard collection implementations provide constructors that take another collection as an argument adding all elements to the new collection at the time of construction:

List list = new ArrayList<>(data);    // will add data as is
Set set1 = new HashSet<>(data);       // will add data keeping only unique values
SortedSet set2 = new TreeSet<>(data); // will add data keeping unique values and sorting
Set set3 = new LinkedHashSet<>(data); // will add data keeping only unique values and preserving the original order

Google Guava Collections framework

Another great framework is Google Guava that is amazing utility class (providing convenience static methods) for construction of different types of standard collections Lists and Sets:

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

List list1 = Lists.newArrayList("ab", "bc", "cd");
List list2 = Lists.newArrayList(data);
Set set4 = Sets.newHashSet(data);
SortedSet set5 = Sets.newTreeSet("bc", "cd", "ab", "bc", "cd");

Mapping Collections
Java Collections framework

Similarly for maps, given a Map map a new map can be constructed with all elements as follows:

Map map1 = new HashMap<>(map);
SortedMap map2 = new TreeMap<>(map);

Apache Commons Collections framework

Using Apache Commons you can create Map using array in ArrayUtils.toMap as well as MapUtils.toMap:

import org.apache.commons.lang3.ArrayUtils;

// Taken from org.apache.commons.lang.ArrayUtils#toMap JavaDoc
// Create a Map mapping colors.
Map colorMap = MapUtils.toMap(new String[][] {{
{"RED", "#FF0000"},
{"GREEN", "#00FF00"},
{"BLUE", "#0000FF"}});

Each element of the array must be either a Map.Entry or an Array, containing at least two elements, where the first element is used as key and the second as value.

Google Guava Collections framework

Utility class from Google Guava framework is named Maps:

import com.google.common.collect.Maps;
…
void howToCreateMapsMethod(Function valueFunction,
         Iterable keys1,
         Set keys2,
         SortedSet keys3) {
    ImmutableMap map1 = toMap(keys1, valueFunction); // Immutable copy
    Map map2 = asMap(keys2, valueFunction); // Live Map view
    SortedMap map3 = toMap(keys3, valueFunction); // Live Map view
}
Version ≥ Java SE 8 Using Stream,
Stream.of("xyz", "abc").collect(Collectors.toList());
or
Arrays.stream("xyz", "abc").collect(Collectors.toList());

Declaring an ArrayList and adding objects

We can create an ArrayList (following the List interface):

List aListOfFruits = new ArrayList();
Version ≥ Java SE 5
List aListOfFruits = new ArrayList();
Version ≥ Java SE 7
List aListOfFruits = new ArrayList<>();

Now, use the method add to add a String:

aListOfFruits.add("Melon");
aListOfFruits.add("Strawberry");

In the above example, the ArrayList will contain the String “Melon” at index 0 and the String “Strawberry” at index 1.

Also we can add multiple elements with addAll(Collection c) method

List aListOfFruitsAndVeggies = new ArrayList();
aListOfFruitsAndVeggies.add("Onion");
aListOfFruitsAndVeggies.addAll(aListOfFruits);

Now “Onion” is placed at 0 index in aListOfFruitsAndVeggies, “Melon” is at index 1 and “Strawberry” is at index 2.

Iterating over Collections
Iterating over List
List names = new ArrayList<>(Arrays.asList("Clementine", "Duran", "Mike"));
Version ≥ Java SE 8
names.forEach(System.out::println);

If we need parallelism use

names.parallelStream().forEach(System.out::println);
Version ≥ Java SE 5
for (String name : names) {
    System.out.println(name);
}
Version < Java SE 5 
for (int i = 0; i < names.size(); i++) {                 
    System.out.println(names.get(i)); 
} 
Version ≥ Java SE 1.2 
//Creates ListIterator which supports both forward as well as backward traversel 
ListIterator listIterator = names.listIterator();

//Iterates list in forward direction
while(listIterator.hasNext()){
     System.out.println(listIterator.next());
}
//Iterates list in backward direction once reaches the last element from above iterator in forward direction
while(listIterator.hasPrevious()){
      System.out.println(listIterator.previous());
}

Iterating over Set

Set names = new HashSet<>(Arrays.asList("Clementine", "Duran", "Mike"));
Version ≥ Java SE 8
names.forEach(System.out::println);
Version ≥ Java SE 5
for (Iterator iterator = names.iterator(); iterator.hasNext(); ) {
     System.out.println(iterator.next());
}
for (String name : names) {
    System.out.println(name);
}
Version < Java SE 5
Iterator iterator = names.iterator();
while (iterator.hasNext()) {
      System.out.println(iterator.next());
}

Iterating over Map

Map names = new HashMap<>();
names.put(1, "Clementine");
names.put(2, "Duran");
names.put(3, "Mike");
Version ≥ Java SE 8
names.forEach((key, value) -> System.out.println("Key: " + key + " Value: " + value));
Version ≥ Java SE 5
for (Map.Entry entry : names.entrySet()) {
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

// Iterating over only keys
for (Integer key : names.keySet()) {
    System.out.println(key);
}
// Iterating over only values
for (String value : names.values()) {
   System.out.println(value);
}
Version < Java SE 5
Iterator entries = names.entrySet().iterator();
while (entries.hasNext()) {
      Map.Entry entry = (Map.Entry) entries.next();
      System.out.println(entry.getKey());
      System.out.println(entry.getValue());
}

LEAVE A REPLY

Please enter your comment!
Please enter your name here