Unit 5
Exception Handling and Generic Programming
Exceptions provide a way to react to exceptional circumstances (like runtime errors) in programs by transferring control to special functions called handlers.
To catch exceptions, a portion of code is placed under exception inspection. This is done by enclosing that portion of code in a try-block. When an exceptional circumstance arises within that block, an exception is thrown that transfers the control to the exception handler. If no exception is thrown, the code continues normally and all handlers are ignored.
An exception is thrown by using the throw keyword from inside the try block. Exception handlers are declared with the keyword catch, which must be placed immediately after the try block:
1 | // exceptions #include <iostream> Usingnamespace std;
Int main () { Try { Throw 20; } Catch (int e) { Cout<< "An exception occurred. Exception Nr. " << e << '\n'; } Return 0; } | An exception occurred. Exception Nr. 20 |
|
The code under exception handling is enclosed in a try block. In this example this code simply throws an exception:
| Throw 20; |
|
A throw expression accepts one parameter (in this case the integer value 20), which is passed as an argument to the exception handler.
The exception handler is declared with the catch keyword immediately after the closing brace of the try block. The syntax for catch is similar to a regular function with one parameter. The type of this parameter is very important, since the type of the argument passed by the throw expression is checked against it, and only in the case they match, the exception is caught by that handler.
Multiple handlers (i.e., catch expressions) can be chained; each one with a different parameter type. Only the handler whose argument type matches the type of the exception specified in the throw statement is executed.
If an ellipsis (...) is used as the parameter of catch, that handler will catch any exception no matter what the type of the exception thrown. This can be used as a default handler that catches all exceptions not caught by other handlers:
1 | Try { // code here } Catch (int param) { cout << "int exception"; } Catch (char param) { cout << "char exception"; } Catch (...) { cout << "default exception"; } |
|
In this case, the last handler would catch any exception thrown of a type that is neither int nor char.
After an exception has been handled the program, execution resumes after the try-catch block, not after the throw statement!.
It is also possible to nest try-catch blocks within more external try blocks. In these cases, we have the possibility that an internal catch block forwards the exception to its external level. This is done with the expression throw; with no arguments. For example:
1 | Try { Try { // code here } Catch (int n) { Throw; } } Catch (...) { Cout << "Exception occurred"; } |
|
Exception specification
Older code may contain dynamic exception specifications. They are now deprecated in C++, but still supported. A dynamic exception specification follows the declaration of a function, appending a throw specifier to it. For example:
| Double myfunction (char param) throw (int); |
|
This declares a function called myfunction, which takes one argument of type char and returns a value of type double. If this function throws an exception of some type other than int, the function calls std::unexpected instead of looking for a handler or callingstd::terminate.
If this throw specifier is left empty with no type, this means that std::unexpected is called for any exception. Functions with no throw specifier (regular functions) never callstd::unexpected, but follow the normal path of looking for their exception handler.
1 | Int myfunction (int param) throw(); // all exceptions call unexpected Int myfunction (int param); // normal exception handling |
|
Standard exceptions
The C++ Standard library provides a base class specifically designed to declare objects to be thrown as exceptions. It is called std::exception and is defined in the <exception> header. This class has a virtual member function called what that returns a null-terminated character sequence (of type char *) and that can be overwritten in derived classes to contain some sort of description of the exception.
1 | // using standard exceptions #include <iostream> #include <exception> Usingnamespace std;
Class myexception: public exception { Virtualconstchar* what() constthrow() { Return "My exception happened"; } } myex;
Int main () { Try { Throw myex; } Catch (exception& e) { Cout << e.what() << '\n'; } Return 0; } | My exception happened. |
|
We have placed a handler that catches exception objects by reference (notice the ampersand & after the type), therefore this catches also classes derived from exception, like our myex object of type myexception.
All exceptions thrown by components of the C++ Standard library throw exceptions derived from this exception class. These are:
Exception | Description |
Bad_alloc | Thrown by new on allocation failure |
Bad_cast | Thrown by dynamic_cast when it fails in a dynamic cast |
Bad_exception | Thrown by certain dynamic exception specifiers |
Bad_typeid | Thrown by typeid |
Bad_function_call | Thrown by empty function objects |
Bad_weak_ptr | Thrown by shared_ptr when passed a bad weak_ptr |
Also deriving from exception, header <exception> defines two generic exception types that can be inherited by custom exceptions to report errors:
Exception | Description |
Logic_error | Error related to the internal logic of the program |
Runtime_error | Error detected during runtime |
A typical example where standard exceptions need to be checked for is on memory allocation:
1 | // bad_alloc standard exception #include <iostream> #include <exception> Usingnamespace std;
Int main () { Try { Int* myarray= newint[1000]; } Catch (exception& e) { Cout << "Standard exception: " << e.what() << endl; } Return 0; } |
|
|
The exception that may be caught by the exception handler in this example is a bad_alloc. Because bad_alloc is derived from the standard base class exception, it can be caught (capturing by reference, captures all related classes).
We face different kinds of errors. These errors can be categorized into five different types. These are like below −
- Syntax Error
- Run-Time Error
- Linker Error
- Logical Error
- Semantic Error
Let us see these errors one by one −
Syntax error
This kind of errors are occurred, when it violates the rule of C++ writing techniques or syntaxes. This kind of errors are generally indicated by the compiler before compilation. Sometimes these are known as compile time error.
In this example, we will see how to get syntax error if we do not put semicolon after one line.
Example
#include<stdio.h>
Main() {
Printf("Hello World")
}
Output
Error] expected ';' before '}' token
Rumtime error
This kind of errors are occurred, when the program is executing. As this is not compilation error, so the compilation will be successfully done. We can check this error if we try to divide a number with 0.
Example
#include<stdio.h>
Main() {
Int x = 52;
Int y = 0;
Printf("Div : %f", x/y);
}
Output
Program crashes during runtime.
Linker error
This kind of errors are occurred, when the program is compiled successfully, and trying to link the different object file with the main object file. When this error is occurred, the executable is not generated, For example some wrong function prototyping, incorrect header file etc. If the main() is written as Main(), this will generate linked error.
Example
#include<stdio.h>
Main() {
Int x = 52;
Int y = 0;
Printf("Div : %f", x/y);
}
Output
C:\crossdev\src\mingw-w64-v3-git\mingw-w64-crt\crt\crt0_c.cundefined reference to WinMain'
Logical error
Sometimes, we may not get the desired output. If the syntax and other things are correct, then also, we may not get correct output due to some logical issues. These are called the logical error. Sometimes, we put a semicolon after a loop, that is syntactically correct, but will create one blank loop. In that case, it will show desired output.
Example
#include<stdio.h>
Main() {
Int i;
For(i = 0; i<5; i++); {
Printf("Hello World");
}
}
Output
Here we want the line will be printed five times. But only one time it will be printed for the block of code.
Semantic error
This kind of error occurs when it is syntactically correct but has no meaning. This is like grammatical mistakes. If some expression is given at the left side of assignment operator, this may generate semantic error.
Example
#include<stdio.h>
Main() {
Int x, y, z;
x = 10;
y = 20;
x + y = z;
}
Output
[Error] lvalue required as left operand of assignment
An exception is a problem that arises during the execution of a program. A C++ exception is a response to an exceptional circumstance that arises while a program is running, such as an attempt to divide by zero.
Exceptions provide a way to transfer control from one part of a program to another. C++ exception handling is built upon three keywords: try, catch, and throw.
- Throw − A program throws an exception when a problem shows up. This is done using a throw keyword.
- Catch − A program catches an exception with an exception handler at the place in a program where you want to handle the problem. The catch keyword indicates the catching of an exception.
- Try − A try block identifies a block of code for which particular exceptions will be activated. It's followed by one or more catch blocks.
Assuming a block will raise an exception, a method catches an exception using a combination of the try and catch keywords. A try/catch block is placed around the code that might generate an exception. Code within a try/catch block is referred to as protected code, and the syntax for using try/catch as follows −
Try {
// protected code
} catch( ExceptionName e1 ) {
// catch block
} catch( ExceptionName e2 ) {
// catch block
} catch( ExceptionName eN ) {
// catch block
}
You can list down multiple catch statements to catch different type of exceptions in case your try block raises more than one exception in different situations.
Throwing Exceptions
Exceptions can be thrown anywhere within a code block using throw statement. The operand of the throw statement determines a type for the exception and can be any expression and the type of the result of the expression determines the type of exception thrown.
Following is an example of throwing an exception when dividing by zero condition occurs −
Double division(int a, int b) {
If( b == 0 ) {
Throw "Division by zero condition!";
}
Return (a/b);
}
Catching Exceptions
The catch block following the try block catches any exception. You can specify what type of exception you want to catch and this is determined by the exception declaration that appears in parentheses following the keyword catch.
Try {
// protected code
} catch( ExceptionName e ) {
// code to handle ExceptionName exception
}
Above code will catch an exception of ExceptionName type. If you want to specify that a catch block should handle any type of exception that is thrown in a try block, you must put an ellipsis, ..., between the parentheses enclosing the exception declaration as follows −
Try {
// protected code
} catch(...) {
// code to handle any exception
}
The following is an example, which throws a division by zero exception and we catch it in catch block.
#include <iostream>
Using namespace std;
Double division(int a, int b) {
If( b == 0 ) {
Throw "Division by zero condition!";
}
Return (a/b);
}
Int main () {
Int x = 50;
Int y = 0;
Double z = 0;
Try {
z = division(x, y);
Cout<< z << endl;
} catch (const char* msg) {
Cerr<< msg << endl;
}
Return 0;
}
Because we are raising an exception of type const char*, so while catching this exception, we have to use const char* in catch block. If we compile and run above code, this would produce the following result −
Division by zero condition!
C++ Standard Exceptions
C++ provides a list of standard exceptions defined in <exception> which we can use in our programs. These are arranged in a parent-child class hierarchy shown below −
Here is the small description of each exception mentioned in the above hierarchy −
Sr.No | Exception & Description |
1 | Std::exception An exception and parent class of all the standard C++ exceptions. |
2 | Std::bad_alloc This can be thrown by new. |
3 | Std::bad_cast This can be thrown by dynamic_cast. |
4 | Std::bad_exception This is useful device to handle unexpected exceptions in a C++ program. |
5 | Std::bad_typeid This can be thrown by typeid. |
6 | Std::logic_error An exception that theoretically can be detected by reading the code. |
7 | Std::domain_error This is an exception thrown when a mathematically invalid domain is used. |
8 | Std::invalid_argument This is thrown due to invalid arguments. |
9 | Std::length_error This is thrown when a too big std::string is created. |
10 | Std::out_of_range This can be thrown by the 'at' method, for example a std::vector and std::bitset<>::operator[](). |
11 | Std::runtime_error An exception that theoretically cannot be detected by reading the code. |
12 | Std::overflow_error This is thrown if a mathematical overflow occurs. |
13 | Std::range_error This is occurred when you try to store a value which is out of range. |
14 | Std::underflow_error This is thrown if a mathematical underflow occurs. |
Define New Exceptions
You can define your own exceptions by inheriting and overriding exception class functionality. Following is the example, which shows how you can use std::exception class to implement your own exception in standard way −
#include <iostream>
#include <exception>
Using namespace std;
Struct MyException : public exception {
Const char * what () const throw () {
Return "C++ Exception";
}
};
Int main() {
Try {
Throw MyException();
} catch(MyException& e) {
Std::cout << "MyException caught" << std::endl;
Std::cout <<e.what() << std::endl;
} catch(std::exception& e) {
//Other errors
}
}
This would produce the following result −
MyException caught
C++ Exception
Here, what() is a public method provided by exception class and it has been overridden by all the child exception classes. This returns the cause of an exception.
Exceptions are the runtime errors or anomalies that a program may encounter while program execution and stops the normal execution of program. Anomalies can consist conditions such as division by zero, access to array element out of found, running out of memory etc.
Exception handling helps to manage the execution of program when an exception occurs during runtime in a systematic manner. Exception handing mechanism allows smooth execution of program. Exception handling mechanisms in C++ is basically done using three keywords, try, throw and catch.
Usually, the exception is handled by the try-catch block, but there are instances where there isn’t a matching catch block and the program just terminates. This terminate() function is modifiable as per user requirements.
Example
#include <exception>
#include <iostream>
Using namespace std;
//defining custom terminator
Void myhandler(){
Cout<< "Inside new terminate handler\n";
Abort();
}
Int main(){
Set_terminate(myhandler);
Try {
Cout<< "Inside try block\n";
Throw 100;
}
Catch (char a){
Cout<< "Inside catch block\n";
}
Return 0;
}
Output
Inside try block
Inside new terminate handler
Exception handling is performed using try/catch statement. The C++ try block is used to place the code that may occur exception. The catch block is used to handle the exception.
Example without try/catch
#include <iostream>
Using namespace std;
Float division(int x, int y) {
Return (x/y);
}
Int main () {
Int i = 50;
Int j = 0;
Float k = 0;
k = division(i, j);
Cout << k << endl;
Return 0;
}
Output:
Floating point exception (core dumped)
Try/catch example
#include <iostream>
Using namespace std;
Float division(int x, int y) {
If( y == 0 ) {
Throw "Attempted to divide by zero!";
}
Return (x/y);
}
Int main () {
Int i = 25;
Int j = 0;
Float k = 0;
Try {
k = division(i, j);
Cout << k << endl;
}catch (const char* e) {
Cerr << e << endl;
}
Return 0;
}
Output:
Attempted to divide by zero!
Multiple catch blocks are used when we have to catch a specific type of exception out of many possible type of exceptions i.e. an exception of type char or int or short or long etc. Let's see the use of multiple catch blocks with an example.
Multiple Catch Block Example
#include<iostream>
Using namespace std;
Int main()
{
Int a=10, b=0, c;
Try
{
//if a is divided by b(which has a value 0);
If(b==0)
Throw(c);
Else
c=a/b;
}
Catch(char c) //catch block to handle/catch exception
{
Cout<<"Caught exception : char type ";
}
Catch(int i) //catch block to handle/catch exception
{
Cout<<"Caught exception : int type ";
}
Catch(short s) //catch block to handle/catch exception
{
Cout<<"Caught exception : short type ";
}
Cout<<"\n Hello";
}
Output is -:
Caught exception : int type
Hello
In this code, we have three catch blocks associated with one try block. At the runtime, compiler finds a division-by-zero problem, hence an exception of type int is thrown when a statement enclosed within the try block is executed.
This exception type is matched with the declared exception type in every catch-block(matching starts with the first catch block).
The catch block that matches with type of exception thrown is executed, while the rest of catch blocks are skipped. Let's see how -
- The first catch block has declared an exception of type char, which doesn't match with the exception int thrown by the try block, hence this catch-block is not executed.
- The second catch block declared with an int matches with the exception int thrown by the try block and hence second catch block will be executed.
- As, we have already found the matching catch block, hence, the third catch block with declared exception of type short is skipped, not checked and not executed.
A catch block for all types exceptions
We could declare a catch block which catches all types of exceptions, irrespective of their types. In such catch block, instead of declaring the type of exception it can catch, we are going to use three dots(...) within its parenthesis. Let's see an example.
Let's understand this by an example -
#include<iostream>
Using namespace std;
Int main()
{
Int a=10, b=0, c;
Try
{
//if a is divided by b(which has a value 0);
If(b==0)
Throw(c);
Else
c=a/b;
}
Catch(...) //catch block to handle/catch exception
{
Cout<<"A catch block for all types of exceptions has caught an exception";
}
}
Output
A catch block for all types of exceptions has caught an exception
As you may see in the code above, a single catch block defined with three dots (...) has caught all types of exceptions i.e. an exception of type int, without even declaring it.
Another example
Let us see another example to declare a catch block which catches all types of exceptions, irrespective of their types. In such catch block, instead of declaring a certain type of exception it can catch, we are going to use three dots(...) within its parenthesis.
#include<iostream>
Using namespace std;
Class A
{
Public:
Char ch ='c';
Int i = 1;
Void function1();
Void function2();
};
Void A :: function1()
{
If(ch == 'c')
Throw(ch);
}
Void A :: function2()
{
If(i == 1)
Throw(i);
}
Int main()
{
A ob;
Try
{
Ob.function1();
}
Catch(...) //catch block to handle/catch exception
{
Cout<<"A catch block for all types of exceptions has caught an exception \n";
}
Try
{
Ob.function2();
}
Catch(...) //catch block to handle/catch exception
{
Cout<<"A catch block for all types of exceptions has caught an exception";
}
}
Output
A catch block for all types of exceptions has caught an exception
A catch block for all types of exceptions has caught an exception
When try blocks are nested and a throw occurs in a function called by an inner try block, control is transferred outward through the nested try blocks until the first catch block is found whose argument matches the argument of the throw expression.
For example:
Try
{
Func1();
Try
{
Func2();
}
Catch (spec_err) { /* ... */ }
Func3();
}
Catch (type_err) { /* ... */ }
// if no throw is issued, control resumes here.
In the above example, if spec_err is thrown within the inner try block (in this case, from func2()), the exception is caught by the inner catch block, and, assuming this catch block does not transfer control, func3() is called. If spec_err is thrown after the inner try block (for instance, by func3()), it is not caught and the function terminate() is called. If the exception thrown from func2() in the inner try block is type_err, the program skips out of both try blocks to the second catch block without invoking func3(), because no appropriate catch block exists following the inner try block.
You use a try block to indicate which areas in your program that might throw exceptions you want to handle immediately. You use a function try block to indicate that you want to detect exceptions in the entire body of a function.
Try block syntax
try
{statements } handler
Function try block syntax
try
:member_initializer_list function_body handler
The following code is an example of a function try block with a member initializer, a function try block and a try block:
#include <iostream>
Using namespace std;
Class E {
Public:
Const char* error;
E(const char* arg) : error(arg) { }
};
Class A {
Public:
Int i;
// A function try block with a member
// initializer
A() try : i(0) {
Throw E("Exception thrown in A()");
}
Catch (E& e) {
Cout<< e.error << endl;
}
};
// A function try block
Void f() try {
Throw E("Exception thrown in f()");
}
Catch (E& e) {
Cout<< e.error << endl;
}
Void g() {
Throw E("Exception thrown in g()");
}
Int main() {
f();
// A try block
Try {
g();
}
Catch (E& e) {
Cout<< e.error << endl;
}
Try {
A x;
}
Catch(...) { }
}
See the following output of the above example:
Exception thrown in f()
Exception thrown in g()
Exception thrown in A()
The constructor of class A has a function try block with a member initializer. Function f() has a function try block. The main() function contains a try block.
The new exception can be defined by overriding and inheriting exception class functionality.
User-defined exception example
Let's see the simple example of user-defined exception in which std::exception class is used to define the exception.
- #include <iostream>
- #include <exception>
- Using namespace std;
- Class MyException : public exception{
- Public:
- Const char * what() const throw()
- {
- Return "Attempted to divide by zero!\n";
- }
- };
- Int main()
- {
- Try
- {
- Int x, y;
- Cout << "Enter the two numbers : \n";
- Cin >> x >> y;
- If (y == 0)
- {
- MyException z;
- Throw z;
- }
- Else
- {
- Cout << "x / y = " << x/y << endl;
- }
- }
- Catch(exception& e)
- {
- Cout << e.what();
- }
- }
Output:
Enter the two numbers :
10
2
x / y = 5
Output:
Enter the two numbers :
10
0
Attempted to divide by zero!
-->
Note: In above example what() is a public method provided by the exception class. It is used to return the cause of an exception.
Generics is the idea to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes and interfaces. For example, classes like an array, map, etc, which can be used using generics very efficiently. We can use them for any type.
The method of Generic Programming is implemented to increase the efficiency of the code. Generic Programming enables the programmer to write a general algorithm which will work with all data types. It eliminates the need to create different algorithms if the data type is an integer, string or a character.
The advantages of Generic Programming are
- Code Reusability
- Avoid Function Overloading
- Once written it can be used for multiple times and cases.
Generics can be implemented in C++ using Templates. Template is a simple and yet very powerful tool in C++. The simple idea is to pass data type as a parameter so that we don’t need to write the same code for different data types. For example, a software company may need sort() for different data types. Rather than writing and maintaining the multiple codes, we can write one sort() and pass data type as a parameter.
Generic Functions using Template:
We write a generic function that can be used for different data types. Examples of function templates are sort(), max(), min(), printArray()
#include <iostream> Usingnamespacestd;
// One function works for all data types. // This would work even for user defined types // if operator '>' is overloaded Template<typenameT>
T myMax(T x, T y) { Return(x > y) ? x : y; }
Intmain() {
// Call myMax for int Cout << myMax<int>(3, 7) << endl;
// call myMax for double Cout << myMax<double>(3.0, 7.0) << endl;
// call myMax for char Cout << myMax<char>('g', 'e') << endl;
Return0; } |
Output:
7
7
g
Generic Class using Template:
Like function templates, class templates are useful when a class defines something that is independent of data type. Can be useful for classes like LinkedList, binary tree, Stack, Queue, Array, etc.
Following is a simple example of template Array class.
#include <iostream> Usingnamespacestd;
Template<typenameT> ClassArray { Private: T* ptr; Intsize;
Public: Array(T arr[], ints); Voidprint(); };
Template<typenameT> Array<T>::Array(T arr[], ints) { Ptr = newT[s]; Size = s; For(inti = 0; i < size; i++) Ptr[i] = arr[i]; }
Template<typenameT> VoidArray<T>::print() { For(inti = 0; i < size; i++) Cout << " "<< *(ptr + i); Cout << endl; }
Intmain() { Intarr[5] = { 1, 2, 3, 4, 5 }; Array<int> a(arr, 5); a.print(); Return0; } |
Output:
1 2 3 4 5
Working with multi-type Generics:
We can pass more than one data types as arguments to templates. The following example demonstrates the same.
#include <iostream> Usingnamespacestd;
Template<classT, classU> ClassA { T x; U y;
Public: A() { Cout << "Constructor Called"<< endl; } };
Intmain() { A<char, char> a; A<int, double> b; Return0; } |
Output:
Constructor Called
Constructor Called
Introduction to Language Specific
C++ is a general-purpose programming language that was developed as an enhancement of the C language to include object-oriented paradigm. It is an imperative and a compiled language.
C++ is a middle-level language rendering it the advantage of programming low-level (drivers, kernels) and even higher-level applications (games, GUI, desktop apps etc.). The basic syntax and code structure of both C and C++ are the same.
Some of the features & key-points to note about the programming language are as follows:
- Simple: It is a simple language in the sense that programs can be broken down into logical units and parts, has a rich libray support and a variety of data-types.
- Machine Independent but Platform Dependent: A C++ executable is not platform-independent (compiled programs on Linux won’t run on Windows), however they are machine independent.
- Mid-level language: It is a mid-level language as we can do both systems-programming (drivers, kernels, networking etc.) and build large-scale user applications (Media Players, Photoshop, Game Engines etc.)
- Rich library support: Has a rich library support (Both standard ~ built-in data structures, algorithms etc.) as well 3rd party libraries (e.g. Boost libraries) for fast and rapid development.
- Speed of execution: C++ programs excel in execution speed. Since, it is a compiled language, and also hugely procedural. Newer languages have extra in-built default features such as grabage-collection, dynamic typing etc. which slow the execution of the program overall. Since there is no additional processing overhead like this in C++, it is blazing fast.
- Pointer and direct Memory-Access: C++ provides pointer support which aids users to directly manipulate storage address. This helps in doing low-level programming (where one might need to have explicit control on the storage of variables).
- Object-Oriented: One of the strongest points of the language which sets it apart from C. Object-Oriented support helps C++ to make maintainable and extensible programs. i.e. Large-scale applications can be built. Procedural code becomes difficult to maintain as code-size grows.
- Compiled Language: C++ is a compiled language, contributing to its speed.
Applications of C++:
C++ finds varied usage in applications such as:
- Operating Systems & Systems Programming. e.g. Linux-based OS (Ubuntu etc.)
- Browsers (Chrome & Firefox)
- Graphics & Game engines (Photoshop, Blender, Unreal-Engine)
- Database Engines (MySQL, MongoDB, Redis etc.)
- Cloud/Distributed Systems
Some interesting facts:
Here are some awesome facts about C++ that may interest you:
- The name of C++ signifies the evolutionary nature of the changes from C. “++” is the C increment operator.
- C++ is one of the predominant languages for the development of all kind of technical and commercial software.
- C++ introduces Object-Oriented Programming, not present in C. Like other things, C++ supports the four primary features of OOP: encapsulation, polymorphism, abstraction, and inheritance.
- C++ got the OOP features from Simula67 Programming language.
- A function is a minimum requirement for a C++ program to run.(at least main() function)
The Collection in Java is a framework that provides an architecture to store and manipulate the group of objects.
Java Collections can achieve all the operations that you perform on a data such as searching, sorting, insertion, manipulation, and deletion.
Java Collection means a single unit of objects. Java Collection framework provides many interfaces (Set, List, Queue, Deque) and classes (ArrayList, Vector, LinkedList, PriorityQueue, HashSet, LinkedHashSet, TreeSet).
What is Collection in Java
A Collection represents a single unit of objects, i.e., a group.
What is a framework in Java
- It provides readymade architecture.
- It represents a set of classes and interfaces.
- It is optional.
What is Collection framework
The Collection framework represents a unified architecture for storing and manipulating a group of objects. It has:
- Interfaces and its implementations, i.e., classes
- Algorithm
Do You Know?
- What are the two ways to iterate the elements of a collection?
- What is the difference between ArrayList and LinkedList classes in collection framework?
- What is the difference between ArrayList and Vector classes in collection framework?
- What is the difference between HashSet and HashMap classes in collection framework?
- What is the difference between HashMap and Hashtable class?
- What is the difference between Iterator and Enumeration interface in collection framework?
- How can we sort the elements of an object? What is the difference between Comparable and Comparator interfaces?
- What does the hashcode() method?
- What is the difference between Java collection and Java collections?
Hierarchy of Collection Framework
Let us see the hierarchy of Collection framework. The java.util package contains all the classes and interfaces for the Collection framework.
Methods of Collection interface
There are many methods declared in the Collection interface. They are as follows:
No. | Method | Description |
1 | Public boolean add(E e) | It is used to insert an element in this collection. |
2 | Public boolean addAll(Collection<? extends E> c) | It is used to insert the specified collection elements in the invoking collection. |
3 | Public boolean remove(Object element) | It is used to delete an element from the collection. |
4 | Public boolean removeAll(Collection<?> c) | It is used to delete all the elements of the specified collection from the invoking collection. |
5 | Default boolean removeIf(Predicate<? super E> filter) | It is used to delete all the elements of the collection that satisfy the specified predicate. |
6 | Public boolean retainAll(Collection<?> c) | It is used to delete all the elements of invoking collection except the specified collection. |
7 | Public int size() | It returns the total number of elements in the collection. |
8 | Public void clear() | It removes the total number of elements from the collection. |
9 | Public boolean contains(Object element) | It is used to search an element. |
10 | Public boolean containsAll(Collection<?> c) | It is used to search the specified collection in the collection. |
11 | Public Iterator iterator() | It returns an iterator. |
12 | Public Object[] toArray() | It converts collection into array. |
13 | Public <T> T[] toArray(T[] a) | It converts collection into array. Here, the runtime type of the returned array is that of the specified array. |
14 | Public boolean isEmpty() | It checks if collection is empty. |
15 | Default Stream<E> parallelStream() | It returns a possibly parallel Stream with the collection as its source. |
16 | Default Stream<E> stream() | It returns a sequential Stream with the collection as its source. |
17 | Default Spliterator<E> spliterator() | It generates a Spliterator over the specified elements in the collection. |
18 | Public boolean equals(Object element) | It matches two collections. |
19 | Public int hashCode() | It returns the hash code number of the collection. |
Iterator interface
Iterator interface provides the facility of iterating the elements in a forward direction only. |
Methods of Iterator interface
There are only three methods in the Iterator interface. They are:
No. | Method | Description |
1 | Public boolean hasNext() | It returns true if the iterator has more elements otherwise it returns false. |
2 | Public Object next() | It returns the element and moves the cursor pointer to the next element. |
3 | Public void remove() | It removes the last elements returned by the iterator. It is less used. |
Iterable Interface
The Iterable interface is the root interface for all the collection classes. The Collection interface extends the Iterable interface and therefore all the subclasses of Collection interface also implement the Iterable interface.
It contains only one abstract method. i.e.,
- Iterator<T> iterator()
It returns the iterator over the elements of type T.
Collection Interface
The Collection interface is the interface which is implemented by all the classes in the collection framework. It declares the methods that every collection will have. In other words, we can say that the Collection interface builds the foundation on which the collection framework depends.
Some of the methods of Collection interface are Boolean add ( Object obj), Boolean addAll ( Collection c), void clear(), etc. which are implemented by all the subclasses of Collection interface.
List Interface
List interface is the child interface of Collection interface. It inhibits a list type data structure in which we can store the ordered collection of objects. It can have duplicate values.
List interface is implemented by the classes ArrayList, LinkedList, Vector, and Stack.
To instantiate the List interface, we must use :
- List <data-type> list1= new ArrayList();
- List <data-type> list2 = new LinkedList();
- List <data-type> list3 = new Vector();
- List <data-type> list4 = new Stack();
There are various methods in List interface that can be used to insert, delete, and access the elements from the list.
The classes that implement the List interface are given below.
ArrayList
The ArrayList class implements the List interface. It uses a dynamic array to store the duplicate element of different data types. The ArrayList class maintains the insertion order and is non-synchronized. The elements stored in the ArrayList class can be randomly accessed. Consider the following example.
- Import java.util.*;
- Class TestJavaCollection1{
- Public static void main(String args[]){
- ArrayList<String> list=new ArrayList<String>();//Creating arraylist
- List.add("Ravi");//Adding object in arraylist
- List.add("Vijay");
- List.add("Ravi");
- List.add("Ajay");
- //Traversing list through Iterator
- Iterator itr=list.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Ravi
Vijay
Ravi
Ajay
LinkedList
LinkedList implements the Collection interface. It uses a doubly linked list internally to store the elements. It can store the duplicate elements. It maintains the insertion order and is not synchronized. In LinkedList, the manipulation is fast because no shifting is required.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection2{
- Public static void main(String args[]){
- LinkedList<String> al=new LinkedList<String>();
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ravi");
- Al.add("Ajay");
- Iterator<String> itr=al.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Ravi
Vijay
Ravi
Ajay
Vector
Vector uses a dynamic array to store the data elements. It is similar to ArrayList. However, It is synchronized and contains many methods that are not the part of Collection framework.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection3{
- Public static void main(String args[]){
- Vector<String> v=new Vector<String>();
- v.add("Ayush");
- v.add("Amit");
- v.add("Ashish");
- v.add("Garima");
- Iterator<String> itr=v.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Ayush
Amit
Ashish
Garima
Stack
The stack is the subclass of Vector. It implements the last-in-first-out data structure, i.e., Stack. The stack contains all of the methods of Vector class and also provides its methods like boolean push(), boolean peek(), boolean push(object o), which defines its properties.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection4{
- Public static void main(String args[]){
- Stack<String> stack = new Stack<String>();
- Stack.push("Ayush");
- Stack.push("Garvit");
- Stack.push("Amit");
- Stack.push("Ashish");
- Stack.push("Garima");
- Stack.pop();
- Iterator<String> itr=stack.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Ayush
Garvit
Amit
Ashish
Queue Interface
Queue interface maintains the first-in-first-out order. It can be defined as an ordered list that is used to hold the elements which are about to be processed. There are various classes like PriorityQueue, Deque, and ArrayDeque which implements the Queue interface.
Queue interface can be instantiated as:
- Queue<String> q1 = new PriorityQueue();
- Queue<String> q2 = new ArrayDeque();
There are various classes that implement the Queue interface, some of them are given below.
Priority Queue
The Priority Queue class implements the Queue interface. It holds the elements or objects which are to be processed by their priorities. Priority Queue doesn't allow null values to be stored in the queue.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection5{
- Public static void main(String args[]){
- PriorityQueue<String> queue=new PriorityQueue<String>();
- Queue.add("Amit Sharma");
- Queue.add("Vijay Raj");
- Queue.add("JaiShankar");
- Queue.add("Raj");
- System.out.println("head:"+queue.element());
- System.out.println("head:"+queue.peek());
- System.out.println("iterating the queue elements:");
- Iterator itr=queue.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- Queue.remove();
- Queue.poll();
- System.out.println("after removing two elements:");
- Iterator<String> itr2=queue.iterator();
- While(itr2.hasNext()){
- System.out.println(itr2.next());
- }
- }
- }
Output:
Head:Amit Sharma
Head:Amit Sharma
Iterating the queue elements:
Amit Sharma
Raj
JaiShankar
Vijay Raj
After removing two elements:
Raj
Vijay Raj
Deque Interface
Deque interface extends the Queue interface. In Deque, we can remove and add the elements from both the side. Deque stands for a double-ended queue which enables us to perform the operations at both the ends.
Deque can be instantiated as:
- Deque d = new ArrayDeque();
ArrayDeque
ArrayDeque class implements the Deque interface. It facilitates us to use the Deque. Unlike queue, we can add or delete the elements from both the ends.
ArrayDeque is faster than ArrayList and Stack and has no capacity restrictions.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection6{
- Public static void main(String[] args) {
- //Creating Deque and adding elements
- Deque<String> deque = new ArrayDeque<String>();
- Deque.add("Gautam");
- Deque.add("Karan");
- Deque.add("Ajay");
- //Traversing elements
- For (String str : deque) {
- System.out.println(str);
- }
- }
- }
Output:
Gautam
Karan
Ajay
Set Interface
Set Interface in Java is present in java.util package. It extends the Collection interface. It represents the unordered set of elements which doesn't allow us to store the duplicate items. We can store at most one null value in Set. Set is implemented by HashSet, LinkedHashSet, and TreeSet.
Set can be instantiated as:
- Set<data-type> s1 = new HashSet<data-type>();
- Set<data-type> s2 = new LinkedHashSet<data-type>();
- Set<data-type> s3 = new TreeSet<data-type>();
HashSet
HashSet class implements Set Interface. It represents the collection that uses a hash table for storage. Hashing is used to store the elements in the HashSet. It contains unique items.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection7{
- Public static void main(String args[]){
- //Creating HashSet and adding elements
- HashSet<String> set=new HashSet<String>();
- Set.add("Ravi");
- Set.add("Vijay");
- Set.add("Ravi");
- Set.add("Ajay");
- //Traversing elements
- Iterator<String> itr=set.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Vijay
Ravi
Ajay
LinkedHashSet
LinkedHashSet class represents the LinkedList implementation of Set Interface. It extends the HashSet class and implements Set interface. Like HashSet, It also contains unique elements. It maintains the insertion order and permits null elements.
Consider the following example.
- Import java.util.*;
- Public class TestJavaCollection8{
- Public static void main(String args[]){
- LinkedHashSet<String> set=new LinkedHashSet<String>();
- Set.add("Ravi");
- Set.add("Vijay");
- Set.add("Ravi");
- Set.add("Ajay");
- Iterator<String> itr=set.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Ravi
Vijay
Ajay
Sorted Set Interface
SortedSet is the alternate of Set interface that provides a total ordering on its elements. The elements of the SortedSet are arranged in the increasing (ascending) order. The SortedSet provides the additional methods that inhibit the natural ordering of the elements.
The SortedSet can be instantiated as:
- SortedSet<data-type> set = new TreeSet();
Tree Set
Java TreeSet class implements the Set interface that uses a tree for storage. Like HashSet, TreeSet also contains unique elements. However, the access and retrieval time of TreeSet is quite fast. The elements in TreeSet stored in ascending order.
Consider the following example:
- Import java.util.*;
- Public class TestJavaCollection9{
- Public static void main(String args[]){
- //Creating and adding elements
- TreeSet<String> set=new TreeSet<String>();
- Set.add("Ravi");
- Set.add("Vijay");
- Set.add("Ravi");
- Set.add("Ajay");
- //traversing elements
- Iterator<String> itr=set.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Ajay
Ravi
Vijay
Array List Class
Java ArrayList class uses a dynamic array for storing the elements. It is like an array, but there is no size limit. We can add or remove elements anytime. So, it is much more flexible than the traditional array. It is found in the java.util package. It is like the Vector in C++.
The ArrayList in Java can have the duplicate elements also. It implements the List interface so we can use all the methods of List interface here. The ArrayList maintains the insertion order internally.
It inherits the AbstractList class and implements List interface.
The important points about Java ArrayList class are:
- Java ArrayList class can contain duplicate elements.
- Java ArrayList class maintains insertion order.
- Java ArrayList class is non synchronized.
- Java ArrayList allows random access because array works at the index basis.
- In ArrayList, manipulation is little bit slower than the LinkedList in Java because a lot of shifting needs to occur if any element is removed from the array list.
Hierarchy of ArrayList class
As shown in the above diagram, Java ArrayList class extends AbstractList class which implements List interface. The List interface extends the Collection and Iterable interfaces in hierarchical order.
ArrayList class declaration
Let's see the declaration for java.util.ArrayList class.
- Public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
Constructors of ArrayList
Constructor | Description |
ArrayList() | It is used to build an empty array list. |
ArrayList(Collection<? extends E> c) | It is used to build an array list that is initialized with the elements of the collection c. |
ArrayList(int capacity) | It is used to build an array list that has the specified initial capacity. |
Methods of ArrayList
Method | Description |
Void add(int index, E element) | It is used to insert the specified element at the specified position in a list. |
Boolean add(E e) | It is used to append the specified element at the end of a list. |
Boolean addAll(Collection<? extends E> c) | It is used to append all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection's iterator. |
Boolean addAll(int index, Collection<? extends E> c) | It is used to append all the elements in the specified collection, starting at the specified position of the list. |
Void clear() | It is used to remove all of the elements from this list. |
Void ensureCapacity(int requiredCapacity) | It is used to enhance the capacity of an ArrayList instance. |
E get(int index) | It is used to fetch the element from the particular position of the list. |
Boolean isEmpty() | It returns true if the list is empty, otherwise false. |
Iterator() |
|
ListIterator() |
|
Int lastIndexOf(Object o) | It is used to return the index in this list of the last occurrence of the specified element, or -1 if the list does not contain this element. |
Object[] toArray() | It is used to return an array containing all of the elements in this list in the correct order. |
<T> T[] toArray(T[] a) | It is used to return an array containing all of the elements in this list in the correct order. |
Object clone() | It is used to return a shallow copy of an ArrayList. |
Boolean contains(Object o) | It returns true if the list contains the specified element |
Int indexOf(Object o) | It is used to return the index in this list of the first occurrence of the specified element, or -1 if the List does not contain this element. |
E remove(int index) | It is used to remove the element present at the specified position in the list. |
Boolean remove(Object o) | It is used to remove the first occurrence of the specified element. |
Boolean removeAll(Collection<?>c) | It is used to remove all the elements from the list. |
Boolean removeIf(Predicate<? super E> filter) | It is used to remove all the elements from the list that satisfies the given predicate. |
Protected void removeRange(int fromIndex, int toIndex) | It is used to remove all the elements lies within the given range. |
Void replaceAll(UnaryOperator<E> operator) | It is used to replace all the elements from the list with the specified element. |
Void retainAll(Collection<?> c) | It is used to retain all the elements in the list that are present in the specified collection. |
E set(int index, E element) | It is used to replace the specified element in the list, present at the specified position. |
Void sort(Comparator<? super E> c) | It is used to sort the elements of the list on the basis of specified comparator. |
Spliterator<E> spliterator() | It is used to create spliterator over the elements in a list. |
List<E> subList(int fromIndex, int toIndex) | It is used to fetch all the elements lies within the given range. |
Int size() | It is used to return the number of elements present in the list. |
Void trimToSize() | It is used to trim the capacity of this ArrayList instance to be the list's current size. |
Non-generic Vs. Generic Collection
Java collection framework was non-generic before JDK 1.5. Since 1.5, it is generic.
Java new generic collection allows you to have only one type of object in a collection. Now it is type safe so typecasting is not required at runtime.
Let's see the old non-generic example of creating java collection.
- ArrayList list=new ArrayList();//creating old non-generic arraylist
Let's see the new generic example of creating java collection.
- ArrayList<String> list=new ArrayList<String>();//creating new generic arraylist
In a generic collection, we specify the type in angular braces. Now ArrayList is forced to have the only specified type of objects in it. If you try to add another type of object, it gives compile time error.
ArrayList Example
- Import java.util.*;
- Public class ArrayListExample1{
- Public static void main(String args[]){
- ArrayList<String> list=new ArrayList<String>();//Creating arraylist
- List.add("Mango");//Adding object in arraylist
- List.add("Apple");
- List.add("Banana");
- List.add("Grapes");
- //Printing the arraylist object
- System.out.println(list);
- }
- }
Output:
[Mango, Apple, Banana, Grapes]
Iterating ArrayList using Iterator
Let's see an example to traverse ArrayList elements using the Iterator interface.
- Import java.util.*;
- Public class ArrayListExample2{
- Public static void main(String args[]){
- ArrayList<String> list=new ArrayList<String>();//Creating arraylist
- List.add("Mango");//Adding object in arraylist
- List.add("Apple");
- List.add("Banana");
- List.add("Grapes");
- //Traversing list through Iterator
- Iterator itr=list.iterator();//getting the Iterator
- While(itr.hasNext()){//check if iterator has the elements
- System.out.println(itr.next());//printing the element and move to next
- }
- }
- }
Output:
Mango
Apple
Banana
Grapes
Iterating ArrayList using For-each loop
Let's see an example to traverse the ArrayList elements using the for-each loop
- Import java.util.*;
- Public class ArrayListExample3{
- Public static void main(String args[]){
- ArrayList<String> list=new ArrayList<String>();//Creating arraylist
- List.add("Mango");//Adding object in arraylist
- List.add("Apple");
- List.add("Banana");
- List.add("Grapes");
- //Traversing list through for-each loop
- For(String fruit:list)
- System.out.println(fruit);
- }
- }
Output:
Mango
Apple
Banana
Grapes
Get and Set ArrayList
The get() method returns the element at the specified index, whereas the set() method changes the element.
- Import java.util.*;
- Public class ArrayListExample4{
- Public static void main(String args[]){
- ArrayList<String> al=new ArrayList<String>();
- Al.add("Mango");
- Al.add("Apple");
- Al.add("Banana");
- Al.add("Grapes");
- //accessing the element
- System.out.println("Returning element: "+al.get(1));//it will return the 2nd element, because index starts from 0
- //changing the element
- Al.set(1,"Dates");
- //Traversing list
- For(String fruit:al)
- System.out.println(fruit);
- }
- }
Output:
Returning element: Apple
Mango
Dates
Banana
Grapes
How to Sort ArrayList
The java.util package provides a utility class Collections which has the static method sort(). Using the Collections.sort() method, we can easily sort the ArrayList.
- Import java.util.*;
- Class SortArrayList{
- Public static void main(String args[]){
- //Creating a list of fruits
- List<String> list1=new ArrayList<String>();
- List1.add("Mango");
- List1.add("Apple");
- List1.add("Banana");
- List1.add("Grapes");
- //Sorting the list
- Collections.sort(list1);
- //Traversing list through the for-each loop
- For(String fruit:list1)
- System.out.println(fruit);
- System.out.println("Sorting numbers...");
- //Creating a list of numbers
- List<Integer> list2=new ArrayList<Integer>();
- List2.add(21);
- List2.add(11);
- List2.add(51);
- List2.add(1);
- //Sorting the list
- Collections.sort(list2);
- //Traversing list through the for-each loop
- For(Integer number:list2)
- System.out.println(number);
- }
- }
Output:
Apple
Banana
Grapes
Mango
Sorting numbers...
1
11
21
51
Ways to iterate the elements of the collection
There are various ways to traverse the collection elements:
- By Iterator interface.
- By for-each loop.
- By ListIterator interface.
- By for loop.
- By forEach() method.
- By forEachRemaining() method.
Iterating Collection through remaining ways
Let's see an example to traverse the ArrayList elements through other ways
- Import java.util.*;
- Class ArrayList4{
- Public static void main(String args[]){
- ArrayList<String> list=new ArrayList<String>();//Creating arraylist
- List.add("Ravi");//Adding object in arraylist
- List.add("Vijay");
- List.add("Ravi");
- List.add("Ajay");
- System.out.println("Traversing list through List Iterator:");
- //Here, element iterates in reverse order
- ListIterator<String> list1=list.listIterator(list.size());
- While(list1.hasPrevious())
- {
- String str=list1.previous();
- System.out.println(str);
- }
- System.out.println("Traversing list through for loop:");
- For(int i=0;i<list.size();i++)
- {
- System.out.println(list.get(i));
- }
- System.out.println("Traversing list through forEach() method:");
- //The forEach() method is a new feature, introduced in Java 8.
- List.forEach(a->{ //Here, we are using lambda expression
- System.out.println(a);
- });
- System.out.println("Traversing list through forEachRemaining() method:");
- Iterator<String> itr=list.iterator();
- Itr.forEachRemaining(a-> //Here, we are using lambda expression
- {
- System.out.println(a);
- });
- }
- }
Output:
Traversing list through List Iterator:
Ajay
Ravi
Vijay
Ravi
Traversing list through for loop:
Ravi
Vijay
Ravi
Ajay
Traversing list through forEach() method:
Ravi
Vijay
Ravi
Ajay
Traversing list through forEachRemaining() method:
Ravi
Vijay
Ravi
Ajay
User-defined class objects in ArrayList
Let's see an example where we are storing Student class object in an array list.
- Class Student{
- Int rollno;
- String name;
- Int age;
- Student(int rollno,String name,int age){
- This.rollno=rollno;
- This.name=name;
- This.age=age;
- }
- }
- Import java.util.*;
- Class ArrayList5{
- Public static void main(String args[]){
- //Creating user-defined class objects
- Student s1=new Student(101,"Sonoo",23);
- Student s2=new Student(102,"Ravi",21);
- Student s2=new Student(103,"Hanumat",25);
- //creating arraylist
- ArrayList<Student> al=new ArrayList<Student>();
- Al.add(s1);//adding Student class object
- Al.add(s2);
- Al.add(s3);
- //Getting Iterator
- Iterator itr=al.iterator();
- //traversing elements of ArrayList object
- While(itr.hasNext()){
- Student st=(Student)itr.next();
- System.out.println(st.rollno+" "+st.name+" "+st.age);
- }
- }
- }
Output:
101 Sonoo 23
102 Ravi 21
103 Hanumat 25
ArrayList Serialization and Deserialization Example
Let's see an example to serialize an ArrayList object and then deserialize it.
- Import java.io.*;
- Import java.util.*;
- Class ArrayList6 {
- Public static void main(String [] args)
- {
- ArrayList<String> al=new ArrayList<String>();
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ajay");
- Try
- {
- //Serialization
- FileOutputStream fos=new FileOutputStream("file");
- ObjectOutputStream oos=new ObjectOutputStream(fos);
- Oos.writeObject(al);
- Fos.close();
- Oos.close();
- //Deserialization
- FileInputStream fis=new FileInputStream("file");
- ObjectInputStream ois=new ObjectInputStream(fis);
- ArrayList list=(ArrayList)ois.readObject();
- System.out.println(list);
- }catch(Exception e)
- {
- System.out.println(e);
- }
- }
- }
Output:
[Ravi, Vijay, Ajay]
ArrayList example to add elements
Here, we see different ways to add an element.
- Import java.util.*;
- Class ArrayList7{
- Public static void main(String args[]){
- ArrayList<String> al=new ArrayList<String>();
- System.out.println("Initial list of elements: "+al);
- //Adding elements to the end of the list
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ajay");
- System.out.println("After invoking add(E e) method: "+al);
- //Adding an element at the specific position
- Al.add(1, "Gaurav");
- System.out.println("After invoking add(int index, E element) method: "+al);
- ArrayList<String> al2=new ArrayList<String>();
- Al2.add("Sonoo");
- Al2.add("Hanumat");
- //Adding second list elements to the first list
- Al.addAll(al2);
- System.out.println("After invoking addAll(Collection<? extends E> c) method: "+al);
- ArrayList<String> al3=new ArrayList<String>();
- Al3.add("John");
- Al3.add("Rahul");
- //Adding second list elements to the first list at specific position
- Al.addAll(1, al3);
- System.out.println("After invoking addAll(int index, Collection<? extends E> c) method: "+al);
- }
- }
Output:
Initial list of elements: []
After invoking add(E e) method: [Ravi, Vijay, Ajay]
After invoking add(int index, E element) method: [Ravi, Gaurav, Vijay, Ajay]
After invoking addAll(Collection<? extends E> c) method:
[Ravi, Gaurav, Vijay, Ajay, Sonoo, Hanumat]
After invoking addAll(int index, Collection<? extends E> c) method:
[Ravi, John, Rahul, Gaurav, Vijay, Ajay, Sonoo, Hanumat]
ArrayList example to remove elements
Here, we see different ways to remove an element.
- Import java.util.*;
- Class ArrayList8 {
- Public static void main(String [] args)
- {
- ArrayList<String> al=new ArrayList<String>();
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ajay");
- Al.add("Anuj");
- Al.add("Gaurav");
- System.out.println("An initial list of elements: "+al);
- //Removing specific element from arraylist
- Al.remove("Vijay");
- System.out.println("After invoking remove(object) method: "+al);
- //Removing element on the basis of specific position
- Al.remove(0);
- System.out.println("After invoking remove(index) method: "+al);
- //Creating another arraylist
- ArrayList<String> al2=new ArrayList<String>();
- Al2.add("Ravi");
- Al2.add("Hanumat");
- //Adding new elements to arraylist
- Al.addAll(al2);
- System.out.println("Updated list : "+al);
- //Removing all the new elements from arraylist
- Al.removeAll(al2);
- System.out.println("After invoking removeAll() method: "+al);
- //Removing elements on the basis of specified condition
- Al.removeIf(str -> str.contains("Ajay")); //Here, we are using Lambda expression
- System.out.println("After invoking removeIf() method: "+al);
- //Removing all the elements available in the list
- Al.clear();
- System.out.println("After invoking clear() method: "+al);
- }
- }
Output:
An initial list of elements: [Ravi, Vijay, Ajay, Anuj, Gaurav]
After invoking remove(object) method: [Ravi, Ajay, Anuj, Gaurav]
After invoking remove(index) method: [Ajay, Anuj, Gaurav]
Updated list : [Ajay, Anuj, Gaurav, Ravi, Hanumat]
After invoking removeAll() method: [Ajay, Anuj, Gaurav]
After invoking removeIf() method: [Anuj, Gaurav]
After invoking clear() method: []
ArrayList example of retainAll() method
- Import java.util.*;
- Class ArrayList9{
- Public static void main(String args[]){
- ArrayList<String> al=new ArrayList<String>();
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ajay");
- ArrayList<String> al2=new ArrayList<String>();
- Al2.add("Ravi");
- Al2.add("Hanumat");
- Al.retainAll(al2);
- System.out.println("iterating the elements after retaining the elements of al2");
- Iterator itr=al.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output:
Iterating the elements after retaining the elements of al2
Ravi
ArrayList example of isEmpty() method
- Import java.util.*;
- Class ArrayList10{
- Public static void main(String [] args)
- {
- ArrayList<String> al=new ArrayList<String>();
- System.out.println("Is ArrayList Empty: "+al.isEmpty());
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ajay");
- System.out.println("After Insertion");
- System.out.println("Is ArrayList Empty: "+al.isEmpty());
- }
- }
Output:
Is ArrayList Empty: true
After Insertion
Is ArrayList Empty: false
ArrayList Example
Let's see an ArrayList example where we are adding books to list and printing all the books.
- Import java.util.*;
- Class Book {
- Int id;
- String name,author,publisher;
- Int quantity;
- Public Book(int id, String name, String author, String publisher, int quantity) {
- This.id = id;
- This.name = name;
- This.author = author;
- This.publisher = publisher;
- This.quantity = quantity;
- }
- }
- Public class ArrayListExample20 {
- Public static void main(String[] args) {
- //Creating list of Books
- List<Book> list=new ArrayList<Book>();
- //Creating Books
- Book b1=new Book(101,"Let us C","Yashwant Kanetkar","BPB",8);
- Book b2=new Book(102,"Data Communications and Networking","Forouzan","Mc Graw Hill",4);
- Book b3=new Book(103,"Operating System","Galvin","Wiley",6);
- //Adding Books to list
- List.add(b1);
- List.add(b2);
- List.add(b3);
- //Traversing list
- For(Book b:list){
- System.out.println(b.id+" "+b.name+" "+b.author+" "+b.publisher+" "+b.quantity);
- }
- }
- }
Output:
101 Let us C Yashwant Kanetkar BPB 8
102 Data Communications and Networking Forouzan Mc Graw Hill 4
103 Operating System Galvin Wiley 6
Linked List class
Java LinkedList class uses a doubly linked list to store the elements. It provides a linked-list data structure. It inherits the AbstractList class and implements List and Deque interfaces.
The important points about Java LinkedList are:
- Java LinkedList class can contain duplicate elements.
- Java LinkedList class maintains insertion order.
- Java LinkedList class is non synchronized.
- In Java LinkedList class, manipulation is fast because no shifting needs to occur.
- Java LinkedList class can be used as a list, stack or queue.
Hierarchy of LinkedList class
As shown in the above diagram, Java LinkedList class extends AbstractSequentialList class and implements List and Deque interfaces.
Doubly Linked List
In the case of a doubly linked list, we can add or remove elements from both sides.
LinkedList class declaration
Let's see the declaration for java.util.LinkedList class.
- Public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
Constructors of LinkedList
Constructor | Description |
LinkedList() | It is used to construct an empty list. |
LinkedList(Collection<? extends E> c) | It is used to construct a list containing the elements of the specified collection, in the order, they are returned by the collection's iterator. |
Methods of LinkedList
Method | Description |
Boolean add(E e) | It is used to append the specified element to the end of a list. |
Void add(int index, E element) | It is used to insert the specified element at the specified position index in a list. |
Boolean addAll(Collection<? extends E> c) | It is used to append all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection's iterator. |
Boolean addAll(Collection<? extends E> c) | It is used to append all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection's iterator. |
Boolean addAll(int index, Collection<? extends E> c) | It is used to append all the elements in the specified collection, starting at the specified position of the list. |
Void addFirst(E e) | It is used to insert the given element at the beginning of a list. |
Void addLast(E e) | It is used to append the given element to the end of a list. |
Void clear() | It is used to remove all the elements from a list. |
Object clone() | It is used to return a shallow copy of an ArrayList. |
Boolean contains(Object o) | It is used to return true if a list contains a specified element. |
Iterator<E> descendingIterator() | It is used to return an iterator over the elements in a deque in reverse sequential order. |
E element() | It is used to retrieve the first element of a list. |
E get(int index) | It is used to return the element at the specified position in a list. |
E getFirst() | It is used to return the first element in a list. |
E getLast() | It is used to return the last element in a list. |
Int indexOf(Object o) | It is used to return the index in a list of the first occurrence of the specified element, or -1 if the list does not contain any element. |
Int lastIndexOf(Object o) | It is used to return the index in a list of the last occurrence of the specified element, or -1 if the list does not contain any element. |
ListIterator<E> listIterator(int index) | It is used to return a list-iterator of the elements in proper sequence, starting at the specified position in the list. |
Boolean offer(E e) | It adds the specified element as the last element of a list. |
Boolean offerFirst(E e) | It inserts the specified element at the front of a list. |
Boolean offerLast(E e) | It inserts the specified element at the end of a list. |
E peek() | It retrieves the first element of a list |
E peekFirst() | It retrieves the first element of a list or returns null if a list is empty. |
E peekLast() | It retrieves the last element of a list or returns null if a list is empty. |
E poll() | It retrieves and removes the first element of a list. |
E pollFirst() | It retrieves and removes the first element of a list, or returns null if a list is empty. |
E pollLast() | It retrieves and removes the last element of a list, or returns null if a list is empty. |
E pop() | It pops an element from the stack represented by a list. |
Void push(E e) | It pushes an element onto the stack represented by a list. |
E remove() | It is used to retrieve and removes the first element of a list. |
E remove(int index) | It is used to remove the element at the specified position in a list. |
Boolean remove(Object o) | It is used to remove the first occurrence of the specified element in a list. |
E removeFirst() | It removes and returns the first element from a list. |
Boolean removeFirstOccurrence(Object o) | It is used to remove the first occurrence of the specified element in a list (when traversing the list from head to tail). |
E removeLast() | It removes and returns the last element from a list. |
Boolean removeLastOccurrence(Object o) | It removes the last occurrence of the specified element in a list (when traversing the list from head to tail). |
E set(int index, E element) | It replaces the element at the specified position in a list with the specified element. |
Object[] toArray() | It is used to return an array containing all the elements in a list in proper sequence (from first to the last element). |
<T> T[] toArray(T[] a) | It returns an array containing all the elements in the proper sequence (from first to the last element); the runtime type of the returned array is that of the specified array. |
Int size() | It is used to return the number of elements in a list. |
LinkedList Example
- Import java.util.*;
- Public class LinkedList1{
- Public static void main(String args[]){
- LinkedList<String> al=new LinkedList<String>();
- Al.add("Ravi");
- Al.add("Vijay");
- Al.add("Ravi");
- Al.add("Ajay");
- Iterator<String> itr=al.iterator();
- While(itr.hasNext()){
- System.out.println(itr.next());
- }
- }
- }
Output: Ravi
Vijay
Ravi
Ajay
LinkedList example to add elements
Here, we see different ways to add elements.
- Import java.util.*;
- Public class LinkedList2{
- Public static void main(String args[]){
- LinkedList<String> ll=new LinkedList<String>();
- System.out.println("Initial list of elements: "+ll);
- Ll.add("Ravi");
- Ll.add("Vijay");
- Ll.add("Ajay");
- System.out.println("After invoking add(E e) method: "+ll);
- //Adding an element at the specific position
- Ll.add(1, "Gaurav");
- System.out.println("After invoking add(int index, E element) method: "+ll);
- LinkedList<String> ll2=new LinkedList<String>();
- Ll2.add("Sonoo");
- Ll2.add("Hanumat");
- //Adding second list elements to the first list
- Ll.addAll(ll2);
- System.out.println("After invoking addAll(Collection<? extends E> c) method: "+ll);
- LinkedList<String> ll3=new LinkedList<String>();
- Ll3.add("John");
- Ll3.add("Rahul");
- //Adding second list elements to the first list at specific position
- Ll.addAll(1, ll3);
- System.out.println("After invoking addAll(int index, Collection<? extends E> c) method: "+ll);
- //Adding an element at the first position
- Ll.addFirst("Lokesh");
- System.out.println("After invoking addFirst(E e) method: "+ll);
- //Adding an element at the last position
- Ll.addLast("Harsh");
- System.out.println("After invoking addLast(E e) method: "+ll);
- }
- }
Initial list of elements: []
After invoking add(E e) method: [Ravi, Vijay, Ajay]
After invoking add(int index, E element) method: [Ravi, Gaurav, Vijay, Ajay]
After invoking addAll(Collection<? extends E> c) method:
[Ravi, Gaurav, Vijay, Ajay, Sonoo, Hanumat]
After invoking addAll(int index, Collection<? extends E> c) method:
[Ravi, John, Rahul, Gaurav, Vijay, Ajay, Sonoo, Hanumat]
After invoking addFirst(E e) method:
[Lokesh, Ravi, John, Rahul, Gaurav, Vijay, Ajay, Sonoo, Hanumat]
After invoking addLast(E e) method:
[Lokesh, Ravi, John, Rahul, Gaurav, Vijay, Ajay, Sonoo, Hanumat, Harsh]
LinkedList example to remove elements
Here, we see different ways to remove an element.
- Import java.util.*;
- Public class LinkedList3 {
- Public static void main(String [] args)
- {
- LinkedList<String> ll=new LinkedList<String>();
- Ll.add("Ravi");
- Ll.add("Vijay");
- Ll.add("Ajay");
- Ll.add("Anuj");
- Ll.add("Gaurav");
- Ll.add("Harsh");
- Ll.add("Virat");
- Ll.add("Gaurav");
- Ll.add("Harsh");
- Ll.add("Amit");
- System.out.println("Initial list of elements: "+ll);
- //Removing specific element from arraylist
- Ll.remove("Vijay");
- System.out.println("After invoking remove(object) method: "+ll);
- //Removing element on the basis of specific position
- Ll.remove(0);
- System.out.println("After invoking remove(index) method: "+ll);
- LinkedList<String> ll2=new LinkedList<String>();
- Ll2.add("Ravi");
- Ll2.add("Hanumat");
- // Adding new elements to arraylist
- Ll.addAll(ll2);
- System.out.println("Updated list : "+ll);
- //Removing all the new elements from arraylist
- Ll.removeAll(ll2);
- System.out.println("After invoking removeAll() method: "+ll);
- //Removing first element from the list
- Ll.removeFirst();
- System.out.println("After invoking removeFirst() method: "+ll);
- //Removing first element from the list
- Ll.removeLast();
- System.out.println("After invoking removeLast() method: "+ll);
- //Removing first occurrence of element from the list
- Ll.removeFirstOccurrence("Gaurav");
- System.out.println("After invoking removeFirstOccurrence() method: "+ll);
- //Removing last occurrence of element from the list
- Ll.removeLastOccurrence("Harsh");
- System.out.println("After invoking removeLastOccurrence() method: "+ll);
- //Removing all the elements available in the list
- Ll.clear();
- System.out.println("After invoking clear() method: "+ll);
- }
- }
Initial list of elements: [Ravi, Vijay, Ajay, Anuj, Gaurav, Harsh, Virat, Gaurav, Harsh, Amit]
After invoking remove(object) method: [Ravi, Ajay, Anuj, Gaurav, Harsh, Virat, Gaurav, Harsh, Amit]
After invoking remove(index) method: [Ajay, Anuj, Gaurav, Harsh, Virat, Gaurav, Harsh, Amit]
Updated list : [Ajay, Anuj, Gaurav, Harsh, Virat, Gaurav, Harsh, Amit, Ravi, Hanumat]
After invoking removeAll() method: [Ajay, Anuj, Gaurav, Harsh, Virat, Gaurav, Harsh, Amit]
After invoking removeFirst() method: [Gaurav, Harsh, Virat, Gaurav, Harsh, Amit]
After invoking removeLast() method: [Gaurav, Harsh, Virat, Gaurav, Harsh]
After invoking removeFirstOccurrence() method: [Harsh, Virat, Gaurav, Harsh]
After invoking removeLastOccurrence() method: [Harsh, Virat, Gaurav]
After invoking clear() method: []
LinkedList Example to reverse a list of elements
- Import java.util.*;
- Public class LinkedList4{
- Public static void main(String args[]){
- LinkedList<String> ll=new LinkedList<String>();
- Ll.add("Ravi");
- Ll.add("Vijay");
- Ll.add("Ajay");
- //Traversing the list of elements in reverse order
- Iterator i=ll.descendingIterator();
- While(i.hasNext())
- {
- System.out.println(i.next());
- }
- }
- }
Output: Ajay
Vijay
Ravi
LinkedList Example: Book
- Import java.util.*;
- Class Book {
- Int id;
- String name,author,publisher;
- Int quantity;
- Public Book(int id, String name, String author, String publisher, int quantity) {
- This.id = id;
- This.name = name;
- This.author = author;
- This.publisher = publisher;
- This.quantity = quantity;
- }
- }
- Public class LinkedListExample {
- Public static void main(String[] args) {
- //Creating list of Books
- List<Book> list=new LinkedList<Book>();
- //Creating Books
- Book b1=new Book(101,"Let us C","Yashwant Kanetkar","BPB",8);
- Book b2=new Book(102,"Data Communications & Networking","Forouzan","Mc Graw Hill",4);
- Book b3=new Book(103,"Operating System","Galvin","Wiley",6);
- //Adding Books to list
- List.add(b1);
- List.add(b2);
- List.add(b3);
- //Traversing list
- For(Book b:list){
- System.out.println(b.id+" "+b.name+" "+b.author+" "+b.publisher+" "+b.quantity);
- }
- }
- }
Output:
101 Let us C Yashwant Kanetkar BPB 8
102 Data Communications & Networking Forouzan Mc Graw Hill 4
103 Operating System Galvin Wiley 6
Difference between ArrayList and LinkedList
ArrayList and LinkedList both implements List interface and maintains insertion order. Both are non synchronized classes.
However, there are many differences between ArrayList and LinkedList classes that are given below.
ArrayList | LinkedList |
1) ArrayList internally uses a dynamic array to store the elements. | LinkedList internally uses a doubly linked list to store the elements. |
2) Manipulation with ArrayList is slow because it internally uses an array. If any element is removed from the array, all the bits are shifted in memory. | Manipulation with LinkedList is faster than ArrayList because it uses a doubly linked list, so no bit shifting is required in memory. |
3) An ArrayList class can act as a list only because it implements List only. | LinkedList class can act as a list and queue both because it implements List and Deque interfaces. |
4) ArrayList is better for storing and accessing data. | LinkedList is better for manipulating data. |
References:
1. Object-Oriented Programming and Java by Danny Poo (Author), Derek Kiong (Author), Swarnalatha Ashok (Author)Springer; 2nd ed. 2008 edition (12 October 2007), ISBN-10: 1846289629, ISBN-13: 978-1846289620,2007
2. Java The complete reference, 9th edition, Herbert Schildt, McGraw Hill Education (India) Pvt. Ltd.
3. Object-Oriented Design Using Java, Dale Skrien, McGraw-Hill Publishing, 2008, ISBN - 0077423097, 9780077423094. 4. UML for Java Programmers by Robert C. Martin, Prentice Hall, ISBN 0131428489,2003.