Java Collection Tutorial


Java Collection Framework is unlikely any group of individual objects which are represented as a single unit be it of any type is known as the collection of objects. Earlier in Java, there was no such thing defined which holds true for it so there arises a need in the next versions of any such concept. So in JDK 1.2, a separate framework was defined known as “Collection Framework” which holds all the collection classes and interfaces in it. 

Java-Collections-Framework-Tutorial

The Collection interface (java.util.Collection) and Map interface (java.util.Map) are the two main “root” interfaces of Java collection classes.

What is a Framework?

A framework is a set of classes and interfaces which provide a ready-made architecture. In order to implement a new feature or a class, there is no need to define a framework. However, an optimal object-oriented design always includes a framework with a collection of classes such that all the classes perform the same kind of task.

Need for a Separate Collection Framework

Before Collection Framework(or before JDK 1.2) was introduced, the standard methods for grouping Java objects (or collections) were Arrays or Vectors, or Hashtables. All of these collections had no common interface. Therefore, though the main aim of all the collections is the same, the implementation of all these collections was defined independently and had no correlation among them. And also, it is very difficult for the users to remember all the different methods, syntax, and constructors present in every collection class.

Are you confused between Collection, Collection Interface, Collections Class, Collection Framework?

  • Collection: A group of individual objects that represent a single entity is known as a collection. It is the common word that you used in your daily life. But if we are discussing Java programming language then it will become Java Collection Framework.
  • Collection Framework: To represent a group of objects as a single entity in the Java programming language we need classes and interfaces defined by the Collection Framework. If are from C++ background that you can compare Collection with Containers and Collection Framework with STL(Standard Template Library).
  • Collection Interface: Interfaces specify what a class must do and not how. It is the blueprint of the class. It is the root interface of the Collection Framework that defines the most common methods that can be used for any collection objects. Or you can say it represents the individual object as a single entity.
  • Collections Class: It is present in java.util package and is a member of Collection Framework. This class provides many utility methods for the collection object.

Now you must be wondering why to include such a concept or what difficulties were faced before java version 1.2 that lead birth to the concept of Collection. In order to figure it out, the three main advantages are mentioned below:

  1. Reduces the programming effort as the programmer does not have to worry about designing the Collection class and on the same hand java being object-oriented language advances in achieving abstraction by not writing the Collection class.
  2. Increases program speed and quality as the programmer now is not worried about thinking best implementation for a specific data structure as of now for a programmer scope widens up and at the same time is now only worried about the best implementation that can drastically boost the performance of program irrespective of data structure constraints encountered earlier.
  3. The API that implements these interfaces are now having common sets of methods be it of any interfaces such as Collection, Set, List, and Map.

Note: All these collections can be imported using:

import java.util.*;

However, single classes can also be imported by replacing * with the class name as shown

import java.util.ArrayList;
import java.util.LinkedList;

The following figure illustrates the hierarchy of the collection framework.

Java-Collections-Framework-Hierarchy

Now, discussing and visualizing Collection at the physical level by discussing its hierarchy. The utility package, java.util contains all the classes and interfaces that are required by the collection framework. The collection framework contains an interface named an iterable interface which provides the iterator to iterate through all the collections. This interface is extended by the main collection interface which acts as a root for the collection framework. All the collections extend this collection interface thereby extending the properties of the iterator and the methods of this interface. 

Iterable interface is the root interface for the entire collection framework. The collection interface extends the iterable interface. Therefore, inherently, all the interfaces and classes implement this interface. The main functionality of this interface is to provide an iterator for the collections. 

The following 6 interfaces are described below first later on been discussed with clean java programs as in implementation.

  • Collection interface
  • List interface
  • Queue interface
  • Deque interface (Double-ended queue)
  • Set interface
  • Map

Collection Interface contains all the basic methods which every collection has like adding the data into the collection, removing the data, clearing the data, etc. All these methods are implemented in this interface because these methods are implemented by all the classes irrespective of their style of implementation.

List Interface

It is a child interface of the collection interface. This interface is dedicated to the data of the list type in which we can store all the ordered collections of the objects. This deals with the index or position-specific functions like getting an element or setting an element. It deals with the arrays and lists types of operations like ArrayList, LinkedList, Vector, and Stack.

1. ArrayList provides us with dynamic arrays in Java. The size of an ArrayList is increased automatically if the collection grows or shrinks if the objects are removed from the collection.

For more, go through Java ArrayList

2. LinkedList is class is an implementation of a doubly-linked list data structure.

For more, go through Java LinkedList

3. Vector provides us with dynamic arrays in Java. This is a legacy class. It is a thread-safe class. This is not recommended being used in a single-threaded environment as it might cause extra overheads. However, to overcome this in Vectors place one can readily use ArrayList.

For more, go through Java Vector

4. Stack is a class is based on the basic principle of last-in-first-out. This is a legacy class. This inherits from a Vector class. It is also a thread-safe class. This is not recommended being used in a single-threaded environment as it might cause extra overheads. However, to overcome this in Vectors place one can readily use ArrayDeque.

For more, go through Java Stack

5. AbstractList class in Java provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a Random Access data store (such as an array). For sequential access data (such as a linked list), AbstractSequentialList should be used in preference to this class. It implements the Collection interface and the AbstractCollection class.

6. AbstractSequentialList class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a “sequential access” data store (such as a linked list). For random access data (such as an array), AbstractList should be used in preference to this class. It is used to implement an unmodifiable list, for which one needs to only extend this AbstractList Class and implement only the get() and the size() methods.

Set Interface

It is an unordered collection of objects in which duplicate values cannot be stored. This set interface is implemented by various classes like HashSet, TreeSet, LinkedHashSet, etc.

Set Interface in Java Collections

HashSet is an inherent implementation of the hash table data structure or Hashing. The objects that we insert into the HashSet do not guarantee to be inserted in the same order. The objects are inserted based on their hash code.

For more, go through Java HashSet

AbstractSet class implements the Collection interface and extends the AbstractCollection class. It provides a skeletal implementation of the Set interface. This class does not override any of the implementations from the AbstractCollection class, but merely adds implementations for equals() and hashCode() method.

CopyOnWriteArraySet uses an internal CopyOnWriteArrayList for all of its operations. It was introduced in JDK 1.5, we can say that it is a thread-safe version of Set. To use this class, we need to import it from ‘java.util.concurrent’ package.

ConcurrentHashMap class is introduced in JDK 1.5 belongs to ‘java.util.concurrent’ package, which implements ConcurrentMap as well as to Serializable interface also. ConcurrentHashMap is an enhancement of HashMap as we know that while dealing with Threads in our application HashMap is not a good choice because performance-wise HashMap is not up to the mark.

LinkedHashSet is very similar to a HashSet. The difference is that this uses a doubly linked list to store the data and retains the ordering of the elements.

For more go through Java LinkedHashSet

EnumSet is one of the specialized implementations of the Set interface for use with the enumeration type as it extends AbstractSet class and implements Set Interface. It does not allow null objects and is not synchronized resulting in a high-performance Set even faster than HashSet. Also, It uses a fail-safe iterator, so it won’t throw ConcurrentModificationException if the collection is modified while iterating.

SortedSet Interface

It is present in java.util package extends the Set interface present in the collection framework. It is an interface that implements the mathematical set. This interface contains the methods inherited from the Set interface and adds a feature that stores all the elements in this interface to be stored in a sorted manner.

1. TreeSet class is an implementation of the Self-balancing binary tree-like a red-black tree. The ordering of the elements is maintained by a set using their natural ordering.

For more, go through Java TreeSet

2. NavigableSet class inherits from the SortedSet interface. It behaves like a SortedSet with the exception that we have navigation methods available in addition to the sorting mechanisms of the SortedSet.

3. ConcurrentSkipListSet class implements the Collection interface and the AbstractSet class. It provides a scalable and concurrent version of NavigableSet in Java. The implementation of ConcurrentSkipListSet is based on ConcurrentSkipListMap. The elements in ConcurrentSkipListSet are sorted by default in their natural ordering or by a Comparator provided at set creation time, depending on which constructor is used.

Map Interface

Map is a data structure that supports the key-value pair mapping for the data. It is mainly used in the scenarios where Students are needed to be searched or removed or added according to the roll numbers or in item-frequency scenarios where items are stored along with their frequencies.

Map in Java

HashTable class implements a hash table, which maps keys to values. Any non-null object can be used as a key or as a value. To successfully store and retrieve objects from a hash table, the objects used as keys must implement the hashCode method and the equals method.

For more, go through Java HashTable

HashMap uses a technique called Hashing. It provides the basic implementation of the Map interface of Java. It stores the data in (Key, Value) pairs. To access a value in a HashMap, we must know its key.

For more, go through Java HashMap

LinkedHashMap is very similar to a HashSet because the primary difference is that this uses a doubly linked list to store the data and retains the ordering of the elements.

For more, go through Java LinkedHashMap

ConcurrentHashMap class implements ConcurrentMap as well as Serializable interface also. ConcurrentHashMap is an enhancement of HashMap as we know that while dealing with Threads in our application HashMap is not a good choice because performance-wise HashMap is not up to the mark.

SortedMap interface extends the Map interface and provides a total ordering of its elements (elements can be traversed in sorted order of keys). This class implements the TreeMap interface.

NavigableMap interface belongs tojava.util‘ package, and It is an extension of SortedMap which provides convenient navigation methods like lowerKey, floorKey, ceilingKey, and higherKey, and along with this popular navigation method it also provides ways to create a Sub Map from an existing Map in Java. For Example, headMap whose keys are less than the specified key, tailMap whose keys are greater than the specified key, and a subMap that strictly contains keys that fall between toKey and fromKey.

ConcurrentMap interface been introduced in JDK 1.5 represents a Map that is capable of handling concurrent access to it without affecting the consistency of entries in a map. ConcurrentMap interface present in ‘java.util.concurrent package’. It provides some extra methods apart from what it inherits from the SuperInterface i.e. java.util.Map. It has inherited the Nested Interface Map.Entry<K, V>

TreeMap is used to implement the Map interface and NavigableMap along with the AbstractMap Class. The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used. This proves to be an efficient way of sorting and storing the key-value pairs. The storing order maintained by the TreeMap must be consistent with equals just like any other sorted map, irrespective of the explicit comparators. The TreeMap implementation is not synchronized in the sense that if a map is accessed by multiple threads, concurrently and at least one of the threads modifies the map structurally, it must be synchronized externally.

AbstractMap class directly implements the Map interface to provide a structure to it, by doing so it makes the further implementations easier. As the name suggests AbstractMap is an abstract class by definition, therefore it cannot be used to create objects. The concrete classes that inherit from AbstractMap can be used to create objects.

EnumMap is a specialized implementation of the Map interface for enumeration types. It extends AbstractMap and implements the Map interface in Java. It belongs to ‘java.util’ package. It is an ordered collection, and they are maintained in the natural order of their keys.

Queue Interface

Queue maintains the FIFO(First In First Out) order similar to a real-world queue line. This interface is dedicated to storing all the elements where the order of the elements matter consisting of the PriorityQueue, LinkedList, and ArrayDeque.

ArrayBlockingQueue class is a bounded blocking queue backed by an array. By bounded, it means that the size of the Queue is fixed. Once created, the capacity cannot be changed. Attempts to put an element into a full queue will result in the operation blocking.

For more, go through Java ArrayBlockingQueue

ArrayDeque is implemented in the collection framework provides us with a way to apply a re-sizable array. This is a special kind of array that grows and allows users to add or remove an element from both sides of the queue.

For more, go through Java ArrayDeque

AbstractQueue provides skeletal implementations of some queue operations. The implementations in this class are appropriate when the base implementation does not allow null elements. Methods add, remove, and element are based on offer, poll, and peek, respectively, but throw exceptions instead of indicating failure via false or null returns. 

BlockingQueue interface in Java is added in Java 1.5 along with various other concurrent Utility classes like ConcurrentHashMap, Counting Semaphore, CopyOnWriteArrrayList, etc. BlockingQueue interface supports flow control (in addition to queue) by introducing blocking if either BlockingQueue is full or empty. A thread trying to enqueue an element in a full queue is blocked until some other thread makes space in the queue, either by dequeuing one or more elements or clearing the queue completely. Similarly, it blocks a thread trying to delete from an empty queue until some other threads insert an item. BlockingQueue does not accept a null value. If we try to enqueue the null item, then it throws NullPointerException.

PriorityQueue is based on the priority heap. It is used when the objects are supposed to be processed based on priority. It is known that a queue follows the First-In-First-Out algorithm, but sometimes the elements of the queue are needed to be processed according to the priority and this class is used in these cases.

For more, go through Java PriorityQueue

PriorityBlockingQueue is an unbounded blocking queue that uses the same ordering rules as class PriorityQueue and supplies blocking retrieval operations. Since it is unbounded, adding elements may sometimes fail due to resource exhaustion resulting in OutOfMemoryError. This class does not permit null elements.

DelayQueue class belongs to ‘java.util.concurrent’ package implementing the BlockingQueue interface. DelayQueue is a specialized PriorityQueue that orders elements based on their delay time. It means that only those elements can be taken from the queue whose time has expired.

ConcurrentLinkedQueue class also belongs tojava.util.concurrent’ package. It was introduced in JDK 1.5. It is used to implement Queue with the help of LinkedList concurrently. It is an unbounded thread-safe implementation of Queue which inserts elements at the tail of the Queue in a FIFO(first-in-first-out) fashion. It can be used when an unbounded Queue is shared among many threads. This class does not permit null elements. Iterators are weakly consistent. This class and its iterator implement all of the optional methods of the Queue and Iterator interfaces. 

LinkedBlockingQueue is an optionally-bounded blocking queue based on linked nodes. It means that the LinkedBlockingQueue can be bounded, if its capacity is given, else the LinkedBlockingQueue will be unbounded. The capacity can be given as a parameter to the constructor of LinkedBlockingQueue. This queue orders elements FIFO (first-in-first-out). It means that the head of this queue is the oldest element of the elements present in this queue. The tail of this queue is the newest element of the elements of this queue. The newly inserted elements are always inserted at the tail of the queue, and the queue retrieval operations obtain elements at the head of the queue.

LinkedTransferQueue class again belonging to ‘java.util.concurrent’ package. It implements the TransferQueue and provides an unbounded functionality based on linked nodes. The elements in the LinkedTransferQueue are ordered in FIFO order, with the head pointing to the element that has been on the Queue for the longest time and the tail pointing to the element that has been on the queue for the shortest time. Because of its asynchronous nature, size() traverses the entire collection, so it is not an O(1) time operation. It may also give inaccurate size if this collection is modified during the traversal.

Deque Interface

Deque or double-ended queue is a data structure where we can add and remove the elements from both ends of the queue. This interface extends the queue interface. The class which implements this interface namely are LinkedList and ArrayDeque, and Collection class. Here in the Collection class, it has implementation of basic algorithms like binary search, sort(), max(), min(), reverse(), fill(), etc. These algorithms are implemented on the above-mentioned collection frameworks.

ArrayDeque class in Java provides a way to apply resizable-array in addition to the implementation of the Deque interface. It is also known as Array Double Ended Queue(Array Deck). This is a special kind of array that grows and allows users to add or remove an element from both sides of the queue.

BlockingDeque interface gets its name because it blocks illegal operations such as insertion into a full queue or deletion from an empty queue, all of these properties are inbuilt into the structure of this interface. Since it is a deque (double-ended queue) that is the insertion, deletion, and traversal operations can be performed from both ends. The BlockingDeque is an interface, so we cannot declare any objects with it.

ConcurrentLinkedDeque class implements the Collection interface and the AbstractCollection class. It belongs to java.util.concurrent package. It is used to implement Deque with the help of LinkedList concurrently. It does not permit null elements and iterators and spliterators are weakly consistent.

Now basic knowledge about the collection to write some basic programs invoking the interfaces with the help of object in the collection is over. It’s time a move a bit deeper down so here a new concept is popularly known as Generics comes into play to jump into. Writing codes with little knowledge is dangerous. 

The idea is to allow type (Integer, String, … etc, and user-defined types) to be a parameter to methods, classes, and interfaces. For example, classes like HashSet, ArrayList, HashMap, etc use generics very well. The generic classes can only be used for Non-primitive types and wrapper classes. Here are the major advantages of Generics in Java:

  1. Write once for any primitive data type as in any generic function code can be written down for computation over non-primitive and later on during compilation these data types are passed in a generic function.
  2. It can be class like ArrayList, LinkedList, Stack, Vector, etc. It can be an interface like Set, Map, etc as discussed above, or it can include functions like sort(), max(), min(), etc. Hence, they are extensively used in Collection.
  3. They are type-safe as they commit errors at compile time.

Unlike arrays, different instantiations of a generic type is not compatible with each other, not even explicitly. This incompatibility may be softened by the wildcard if ? is used as an actual type parameter.

The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of parameter, field, or local variable; sometimes as a return type.

User cases

  • Case 1: A simple java program to demonstrate the compile-time errors without the involvement of generics into play. Here ‘ClassCastException‘ is thrown by the program
  • Case 2: Generic concept been introduced though it will not append output illustrating incompatible type-casting of data types.
  • Case 3: Working of a generic function.
  • Case 4: Wildcards been introduced in java but still will throw an exception at the runtime.
  • Case 5: Wildcard implementation in generics

Example:

Java

// Case 1
// A Simple Java program to demonstrate the
// compile-time errors without generic invovement
// ClassCastException

// Importing all classes from
// java.util package
import java.util.*;

// Class 1
// Object class
class GenericClass {

    // Object of this class
    Object x;
    Object y;
}

// Class 2
// Main class
class GFG {
    // Min driver method
    public static void main(String[] args)
    {
        // Creating a generic class object
        // Here 'go' is name of object
        GenericClass go = new GenericClass();

        // Compiles fine because
        // p being an object accepts integer
        go.x = 007;

        // Compiles fine because
        // p being an object accepts string
        go.y = "JamesBond";

        // Forcefully explicitly typecasting the
        // integer value to string
        String str = (String)go.x;

        // This throws a ClassCastException
        // as go.x object  is an integer and
        //  cannot be casted to a string
    }
}

Java

// Case 2
// Java Program in which generic class is used
// and still it will throw out compile-time error

// Importing all classes from
// java.util package
import java.util.*;

// Class 1
// Sample generic class
class GenericClass<T, S> {

    T x;
    S y;
}

// Class 2
// Main class
class GFG {

    // Main driver method
    public static void main(String[] args)
    {

        // Creating an  object of above generic class
        // in the main method()
        // Declaring object of Integer and String type
        GenericClass<Integer, String> go
            = new GenericClass<Integer, String>();

        // It will successfully compile as it
        // is an object will will accept
        // integer and String types
        go.x = 007;
        go.y = "JamesBond";

        // Forcefully explicitly typecasting it to
        // string type which will
        // generate a compiler eror
        String str = (String)go.x;
    }
}

Java

// Case 3
// Java program to illustarte the working of
// generic function

// Importing all classes
// from java.util package
import java.util.*;

// Main class
class GFG {
    // Declaration of generic function
    // Type parameters defined before
    // return type
    public static <T> int count(T arr[], T x)
    {
        // Initially assigning with the zero
        int res = 0;

        // Traversal over the array elements
        // using for-each loop
        for (T e : arr) {
            // If elements is encountered in an array
            if (e.equals(x))

                // Increment the count/frequency
                // of that element
                res++;
        }

        // Return the frequency of the element
        // been searched in an array
        return res;
    }

    // Main driver method
    public static void main(String[] args)
    {
        // Input array
        // Custom inputs of elemnets
        Integer arr[] = { 1, 3, 5, 7, 9, 7, 5, 1, 7, 7, 7 };

        // Print an  display the count of the
        // element passed as a parameter
        System.out.println(count(arr, 7));
    }
}

Java

// Case 4
// Java Program to illustrate the concept of
// wildcards in generics 
// where still the error is thrown

// Importing all classes from
// java.util package
// Importing input output classes
import java.io.*;
import java.util.*;

// Class 1
class GFG {
    // Body
}

// Class 2
class GFGPremium extends GFG {
    // Body
}

// Class 3
class GFGTest {

    // Main driver method
    public static void main(String[] args)
    {

        // Creating a collecton ArrayLists
        // of both the classes
        ArrayList<GFG> gfg = new ArrayList<GFG>();
        ArrayList<GFGPremium> gfgp
            = new ArrayList<GFGPremium>();

        // GFG ArayList is been refered to
        // GFGPremium ArrayList
        gfg = gfgp;

        // It is not allowed and will throw
        // compiler error at runtime

        // Practically do thinking one cant get the access
        // to premium without paying Geek
    }
}

Java

// Case 5
// Java Program to illustrate the concept of
// wildcards in generics 
// where no error is thrown

// Importing all classes from
// java.util package
import java.io.*;
// Importing input output classes
import java.util.*;

// Class 1
class GFG {
    // Body
}

// Class 2
class GFGPremium extends GFG {
    // Body
}

// Class 3
// Main class 
class GFGTest {

    // Main driver method
    public static void main(String[] args)
    {

        // Creating a collecton ArrayLists
        // of both the classes
        ArrayList<?> gfg = new ArrayList<GFG>();
        ArrayList<GFGPremium> gfgp
            = new ArrayList<GFGPremium>();

        // GFG ArayList is been refered to
        // GFGPremium ArrayList
        gfg = gfgp;

        // Now it is allowed allowed and will throw
        // compiler error at runtime

        // Print and display message for 
        // successful execution of the program
        System.out.print("Wildcard in generics is must Geek");

       
    }
}

Output:

Case 1

Case 2

Case 3

Case 4

Case 5

Wildcard in generics is must Geek

Now jumping to the most important topic along with which most concepts revolve during implementation, yes you guess it right geek Collection interface. The Collection is the root interface for most of the interfaces in the Collection framework in Java

Therefore, Collection has sub-interfaces like Set, List, and Queue which implements the Collection interface which in turn is inherited from the Iterable interface. This is the root interface for the entire collection framework. The Collection interface extends the Iterable interface. Therefore, inherently, all the interfaces and classes implement this interface. The main functionality of this interface is to provide an iterator for the collections. Therefore, this interface contains only one abstract method which is the iterator. It returns the 
 

Iterator iterator();

Now, dwelling onto different methods present in Collection such as add(), removes(), removeAll(), contains(), containsAll(), stream() and many more. In order to use so, one has to get a clear understanding of the same. Let’s peek out for various functions causing bulk operations namely

  1. containsAll() Method
  2. addAll() Method
  3. removeAll() Method
  4. retainAll() Method

Function 1: containsAll() Method

boolean containsAll(Collection c)

This method takes a collection c and checks whether all the elements of c is present in this Collection or not. The method returns True if all elements in the collection c, are present in this Collection otherwise it returns False.

Function 2: addAll() Method

boolean addAll(Collection c)

This method is used to append all the elements from the collection c passed as a parameter to the end of this collection keeping in mind the order of return by the collection’s iterator. The method returns true if at least one action of append is performed.

Function 3: removeAll() Method

boolean removeAll(Collection c)

This method is used to remove from this collection all of its elements that are contained in the specified collection c. This method returns true if this list changed as a result of the call. This method uses a wildcard character signifying that a collection of any data types can be passed into the removeAll() method. These are the methods been most frequently used.

Function 4: retainAll() Method

This method of ArrayList is used to remove all the array list’s elements that are not contained in the specified collection or retains all matching elements in the current ArrayList instance that match all elements from the Collection list passed as a parameter to the method.

public boolean retainAll(Collection C)

Rest there are other methods to been put in the usage such as removeIf() and retainAll(). Do visit them thoroughly in order to get a better understanding of them too before we now finally end up this article with iterating through Collections. Let’s try to implement a few of them to get a better understanding of them.

Examples:

Java

// Method 1 - containsAll() 
// Java Program to illustrate
// containsAll() method

// Importing all classes frm
// java.util package
import java.util.*;

// Class
class GfG {

    // Main driver method
    public static void main(String args[])
    {
        // Creating an empty list
        // Declaring an Integer type object
        List<Integer> list1 = new ArrayList<Integer>();

        // Adding custom elements to the List
        // using add() method
        list1.add(10);
        list1.add(20);
        list1.add(30);
        list1.add(40);
        list1.add(50);

        // Creating another empty list of Integer type
        List<Integer> list2 = new ArrayList<Integer>();

        // Again, adding custom elements to the List
        // using add() method
        list2.add(10);
        list2.add(30);

        // Returns true if all the elements of list2
        // is present in list1
        // If not, return false
        System.out.println(list1.containsAll(list2));
    }
}

Java

// Method 2 - addAll() 
// Java program to illustrate 
// boolean addAll(Collection c)

// Importing all classes from
// java.util package  
import java.util.*; 

// Class 
class GFG {

    // Main driver method  
    public static void main(String args[]) 
    { 

        // Creating an empty array list1
        // Declaring an object of Integer type 
        List<Integer> list1 = new ArrayList<Integer>(); 

        // Adding custom elements in the list1 
        // using add() method
        list1.add(10); 
        list1.add(20); 
        list1.add(30); 
        list1.add(40);

        // Again, creating an empty array list2
        // Declaring an object of Integer type
        List<Integer> list2 = new ArrayList<Integer>(5); 

        // Adding custom elements in the list1 
        // using add() method 
        list2.add(40); 
        list2.add(50); 

        // inserting all elements where 
        // list2 will get printed after list1 
        list1.addAll(list2); 
        
        // Print the updated List 
        System.out.println(list1); 
    } 
} 

Java

// Method 3 - reomoveAll() 
// Java program to demonstrate 
// removeAll() method
import java.util.*; 

// Class 
public class GFG { 

    // Main driver method 
    public static void main(String[] argv)
    { 

        // Creating object of ArrayList<Integer> 
        List<Integer> list1 = new ArrayList<Integer>(); 

        // Populating the list1 
        list1.add(10); 
        list1.add(20); 
        list1.add(30); 

        // Creating another object of ArrayList
        // Declaring object of Integer type 
        List<Integer> list2 = new ArrayList<Integer>(); 
        
        // Populating the list1 
        list2.add(10); 
        list2.add(20); 

        // Removing elements from list1 
        // specified in list2 
        // using removeAll() method 
        list1.removeAll(list2); 

        // Printing the list1 
        System.out.println(list1); 
    } 
}

Java

// Method 4 - retainAll() method 
// Java program to demonstrate 
// retainAll() method

// Importing all classes from
// java.util package
import java.util.*; 

// Class
public class GFG {

    // Main driver method  
    public static void main(String[] argv)
    { 
        // Creating an object of ArrayList
        // Declaring object of Integer type 
        List<Integer> list1 = new ArrayList<Integer>(); 

        // Populating the list1
        // Custom inputs  
        list1.add(10); 
        list1.add(20); 
        list1.add(30); 

        // Creating an another object of ArrayList
        // Declaring an object of Integer type  
        List<Integer> list2 = new ArrayList<Integer>(); 
        
        // Populating the list1 
        list2.add(10); 
        list2.add(20); 

        // Retains elements of list1 specified in list2 
        // using retainAll() method 
        list1.retainAll(list2); 

        // Printing the list1 
        System.out.println(list1); 
    } 
}

Output:

Method 1:

true

Method 2:

[10, 20, 30, 40, 40, 50]

Method 3:

[30]

Method 4:

[10, 20]

Now lets us discuss all the methods been proposed for iteration are as follows:

  1. Iterators Method
  2. For-each loop Method
  3. forEach() Method
  4. Streams Method

There are various ways of traversing through a collection namely:

Method 1: Iterators Method

The Iterable interface provides an iterator() method which implements the Iterator interface. The iterator interface provides 3 functions(hasNext(), next(), remove()) which are used to iterate through the collection and also remove elements from the collection. The collection interface extends the iterable interface. Therefore, inherently, all the interfaces and classes implement this interface. The main functionality of this interface is to provide an iterator for the collections. Therefore, this interface contains only one abstract method which is the iterator.

Method 2: For-each loop Method

For-each is another array traversing technique like for loop, while loop, do-while loop introduced in Java5. It’s commonly used to iterate over an array or a Collections class (eg, ArrayList).

for (type var : array) 
{ 
    statements using var;
}

Method 3: forEach() is also present in the Iterable interface. Therefore, it can be implemented by all the collections in the framework that extends the Iterable interface. Whenever is needed to traverse over a collection we have to create an Iterator to iterate over the collection, and then we can have our business logic inside a loop for each of the elements inside the collection.

public void forEach(Consumer action)

Method 4: Streams Method is the most recommended method to traverse through a collection or perform any operation of filter or segregation. This was added in Java 8. There are several benefits of using stream:

  • It can be parallel and infinite.
  • No storage.
  •  Functions can be pipelined.
  • The most important benefit of Streams is that we can create them from the collection, arrays, Files Lines, and even from methods in Stream.

Examples:

Java

// Method 1
// Traversing a collection
// using for-each loop
import java.util.*;

// Main class
class GFG {

    // Main driver method
    public static void main(String[] args)
    {

        // Creating an List object
        // Declaring object of Integer type
        List<Integer> l = Arrays.asList(10, 15, 7, 20, 40);

        // Now, traversal part
        // using for-each method

        // Looking for element x in List
        for (Integer x : l)

            // Print and dispaly all elements
            // in the List
            System.out.print(x + " ");
    }
}

Java

// Method 2
// Traverssing a collection
// using forEach() method

// Importing all classes from
// java.util package
import java.util.*;

// Class
class GFG {
    // Main driver method
    public static void main(String[] args)
    {

        // ArrayList containing elements
        List<Integer> l = Arrays.asList(10, 15, 7, 20, 40);

        // Traversing a collection
        // using forEach() method
        l.forEach(x -> System.out.println(x));
    }
}

Java

// Method 3
// Traversing a collection
// using stream() method

// Importing all classes from
// java.util package
import java.util.*;

// Class
class GFG {
    // Main driver method
    public static void main(String[] args)
    {

        // Creating List object of Integer type
        // Custom input entries
        List<Integer> l = Arrays.asList(10, 15, 7, 20, 40);

        // Traversing a collection
        // using stream method
        // to print elements with a space
        l.stream().forEach(x -> System.out.print(x + " "));

        // New line
        System.out.println();

        // Step 1: Creating a stream over the ArrayList
        // Step 2: Filtering the elements greater than 10
        // Step 3: Filtering the even elements
        // Step 4: Printing the list with space
        l.stream()
            .filter(x -> x > 10)
            .filter(x -> x % 2 == 0)
            .forEach(x -> System.out.print(x + " "));
    }
}

Output:

Method 1:

10 15 7 20 40

Method 2:

10
15
7
20
40

Method 3:

10 15 7 20 40
20 40



  • Last Updated : 26 Sep, 2023

Share your thoughts in the comments
Similar Reads