List Interface
The List
interface is a part of the Java Collections Framework and extends the Collection
interface. It represents an ordered collection of elements where duplicate values are allowed. The key feature of a list is that elements are stored in a specific order, and you can access elements by their index.
Common Implementations of List
Java offers several implementations of the List
interface, each with its own characteristics. The most commonly used List
implementations are:
ArrayList: This implementation uses an array to store elements. It provides fast random access to elements but can be slower for insertions and removals in the middle.
Synchronization: Not synchronized (not thread-safe).
Performance: faster than
Vector
due to lack of synchronization.Data Structure: It uses a dynamic array to store elements, allowing for fast random access but slower insertions/removals in the middle.
Usage: Suitable for most use cases where synchronization is not required.
Dynamic Sizing: Unlike regular arrays in Java,
ArrayList
does not have a fixed size. It can automatically resize itself as elements are added or removed. This makes it very convenient for managing collections of data of varying sizes.Ordered:
ArrayList
maintains the order of elements based on the order in which they were added. You can access elements by their index, and they are stored sequentially in memory.Concurrency:
ArrayList
is not synchronized, meaning it is not thread-safe. If you need to work withArrayList
in a multi-threaded environment, consider usingCollections.synchronizedList()
to create a synchronized version or usejava.util.concurrent
alternatives likeCopyOnWriteArrayList
.Null Elements:
ArrayList
can store null elements. This can be useful but also requires extra care when iterating or performing operations on the list.
LinkedList: In a
LinkedList
, elements are stored as nodes, connected by pointers. It's efficient for insertions and removals but slower for random access.Synchronization: Not synchronized by default (not thread-safe).
Performance: Efficient for insertions and removals, especially at the beginning or end of the list, but slower for random access.
Data Structure: Elements are stored as nodes linked together, which allows for efficient insertions/removals but slower access.
Usage: Useful when frequent insertions/removals are needed, or when implementing certain data structures like queues or double-ended queues (Deque).
Dynamic Sizing: Like
ArrayList
,LinkedList
can grow or shrink as elements are added or removed. However, it does not use a single contiguous block of memory like an array; instead, each element is stored in a separate node, which is linked to the next and previous nodes.Doubly Linked Structure: A
LinkedList
is composed of nodes where each node contains both data and a reference (or link) to the next node (forward link) and the previous node (backward link). This structure allows for efficient insertion and deletion of elements at both the beginning and end of the list.Methods for Adding and Removing:
LinkedList
provides methods for adding (add()
) and removing (remove()
) elements at various positions, including the beginning and end of the list.LinkedList<Integer> numbers = new LinkedList<Integer>(); numbers.add(1); // Adds 1 to the end of the list. numbers.addFirst(0); // Adds 0 to the beginning of the list. numbers.removeFirst(); // Removes the first element.
Accessing Elements: You can access elements by their index using the
get(index)
method. However, accessing elements in the middle of aLinkedList
is less efficient compared to anArrayList
because it requires traversing the list from the beginning or end.String first = names.getFirst(); // Retrieves the first element. String last = names.getLast(); // Retrieves the last element.
Null Elements:
LinkedList
can store null elements, just likeArrayList
.Specialized Variants: Java offers specialized
LinkedList
implementations likejava.util.LinkedList
andjava.util.LinkedList.Node
. The former is a general-purpose doubly linked list, while the latter provides more fine-grained control over individual nodes and can be useful in specific situations.
Vector: Similar to
ArrayList
, but it's synchronized, making it thread-safe. However, this synchronization can introduce performance overhead.Synchronization: Synchronized (thread-safe).
Performance: Slower compared to
ArrayList
.Data Structure: Similar to
ArrayList
, it uses a dynamic array to store elements.Usage: Useful in multi-threaded environments where thread safety is a concern, but it comes with a performance trade-off.
Dynamic Array:
Vector
is essentially a dynamic array, likeArrayList
. It can dynamically grow or shrink to accommodate elements.Enumeration:
Vector
provides an Enumeration interface, which allows you to traverse its elements. This feature is a legacy from earlier versions of Java before iterators were introduced.Enumeration<String> enumeration = names.elements(); while (enumeration.hasMoreElements()) { String name = enumeration.nextElement(); System.out.println(name); }
Stack: A specialized class based on
Vector
, which follows the Last-In-First-Out (LIFO) principle.Extends Vector: As mentioned,
Stack
extends theVector
class, which means it inherits all the methods and properties ofVector
. However, it's important to note thatVector
is considered a legacy class, and its use is discouraged in favor of more modern collections likeArrayList
.LIFO: Follows the Last-In-First-Out (LIFO) principle, making it suitable for implementing stacks and undo/redo functionality.
Basic Operations:
push(E item)
: Adds an element to the top of the stack.pop()
: Removes and returns the top element of the stack. If the stack is empty, it throws anEmptyStackException
.peek()
: Returns the top element of the stack without removing it. If the stack is empty, it throws anEmptyStackException
.empty()
: Checks if the stack is empty.search(Object o)
: Searches for an element in the stack and returns its 1-based position from the top. Returns -1 if the element is not found.Stack<Integer> numbers = new Stack<Integer>(); numbers.push(1); int top = numbers.pop(); // Removes and returns 1.
Usage: Primarily used as a stack data structure.
Iteration: While
Stack
does not provide built-in iterators, you can use thepop()
method in a loop to retrieve elements in reverse order (from top to bottom).Considerations: In modern Java development,
java.util.Stack
is less commonly used in favor of other stack implementations likejava.util.LinkedList
or the built-in stack data structures provided by the Java Collections Framework. These alternatives provide similar functionality without the baggage of being a legacy class. If you need a stack-like data structure, you might consider usingDeque
(double-ended queue) implementations such asjava.util.ArrayDeque
orjava.util.LinkedList
.
Below is a code where I have used some methods of List.
package com.hk.basic;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;
public class ListDemo {
List<String> arrayList;
List<String> linkedList;
List<String> vector;
List<String> stack;
public static void main(String[] args) {
ListDemo demo = new ListDemo();
//create List
demo.arrayList = new ArrayList<String>();
demo.linkedList = new LinkedList<String>();
demo.vector = new Vector<String>();
demo.stack = new Stack<String>();
//add element to list
demo.arrayList.add("arrayList");
demo.arrayList.add("arrayList1");
demo.linkedList.add("linkedList");
demo.linkedList.add("linkedList1");
demo.vector.add("vector");
demo.vector.add("vector1");
demo.stack.add("stack");
demo.stack.add("stack1");
//print all elements
System.out.println(demo.print());
//iterate list
demo.iterateList(demo.arrayList);
demo.iterateList(demo.linkedList);
demo.iterateList(demo.vector);
demo.iterateList(demo.stack);
//size of a list
System.out.println();
System.out.println("arrayList size:" + demo.arrayList.size());
System.out.println("linkedList size:" + demo.linkedList.size());
System.out.println("vector size:" + demo.vector.size());
System.out.println("stack size:" + demo.stack.size());
//accessing the element by index
System.out.println();
System.out.println(demo.arrayList.get(0));
System.out.println(demo.linkedList.get(0));
System.out.println(demo.vector.get(0));
System.out.println(demo.stack.get(0));
//Modifying Elements
demo.arrayList.set(0, "arrayList_new");
demo.linkedList.set(0, "linkedList_new");
demo.vector.set(0, "vector_new");
demo.stack.set(0, "stack_new");
//Removing element
demo.arrayList.remove(1);
demo.linkedList.remove(1);
demo.vector.remove(1);
demo.stack.remove(1);
//print all elements
System.out.println(demo.print());
}
public String print() {
return "\narrayList=" + arrayList + "\nlinkedList=" + linkedList + "\nvector=" + vector + "\nstack="
+ stack + "\n";
}
//Iterate a List
public void iterateList(List<String> list){
for (String element : list) {
System.out.println(element);
}
}
}
This is all about the List interface, in the next blog we will learn about other implementations of the Collection Interface.
Click here to read about the previous blog i.e java collection framework.
Click here to read about my most popular blog i.e binary search.