Collection Interface
The Collection interface is the foundation of the Java Collections Framework, defining the basic operations that all collections should support.
Source Code #
Core Interface Definition #
public interface Collection<E> extends Iterable<E> {
// Query Operations
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// Modification Operations
boolean add(E e);
boolean remove(Object o);
// Bulk Operations
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// Comparison and hashing
boolean equals(Object o);
int hashCode();
// Default methods (Java 8+)
default Stream<E> stream() { ... }
default Stream<E> parallelStream() { ... }
default boolean removeIf(Predicate<? super E> filter) { ... }
}
Implementation Details #
Query Operations #
size() #
int size();
Returns the number of elements in the collection. Should be O(1) for most implementations.
isEmpty() #
boolean isEmpty();
Returns true if the collection contains no elements. Default implementation:
default boolean isEmpty() {
return size() == 0;
}
contains(Object o) #
boolean contains(Object o);
Returns true if the collection contains the specified element. Typically O(n) for lists, O(1) for hash-based collections.
iterator() #
Iterator<E> iterator();
Returns an iterator over the elements. The iterator should be fail-fast for most implementations.
toArray() #
Object[] toArray();
Returns an array containing all elements. The runtime type is Object[].
toArray(T[] a) #
<T> T[] toArray(T[] a);
Returns an array containing all elements, using the specified array if it’s large enough.
Modification Operations #
add(E e) #
boolean add(E e);
Adds the specified element to the collection. Returns true if the collection changed.
remove(Object o) #
boolean remove(Object o);
Removes a single instance of the specified element. Returns true if an element was removed.
Bulk Operations #
containsAll(Collection c) #
boolean containsAll(Collection<?> c);
Returns true if the collection contains all elements in the specified collection.
addAll(Collection<? extends E> c) #
boolean addAll(Collection<? extends E> c);
Adds all elements from the specified collection. Returns true if the collection changed.
removeAll(Collection c) #
boolean removeAll(Collection<?> c);
Removes all elements that are contained in the specified collection.
retainAll(Collection c) #
boolean retainAll(Collection<?> c);
Retains only the elements that are contained in the specified collection (intersection).
clear() #
void clear();
Removes all elements from the collection.
Default Methods (Java 8+) #
stream() #
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
Returns a sequential Stream with this collection as its source.
parallelStream() #
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
Returns a possibly parallel Stream with this collection as its source.
removeIf(Predicate<? super E> filter) #
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
Removes all elements that satisfy the given predicate.
Abstract Collection Implementation #
The AbstractCollection class provides skeletal implementations of the Collection interface:
public abstract class AbstractCollection<E> implements Collection<E> {
protected AbstractCollection() {}
public abstract Iterator<E> iterator();
public abstract int size();
public boolean isEmpty() {
return size() == 0;
}
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o == null) {
while (it.hasNext())
if (it.next() == null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
public Object[] toArray() {
Object[] result = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < result.length; i++)
result[i] = it.next();
return result;
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
Iterator<E> it = iterator();
Object[] result = a;
for (int i = 0; i < result.length; i++) {
if (!it.hasNext()) { // fewer elements than expected
if (a == result) {
result = Arrays.copyOf(result, i);
} else {
result[i] = null; // null-terminate
}
return (T[])result;
}
result[i] = it.next();
}
if (a.length > size)
a[size] = null;
return a;
}
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
Iterator<E> it = iterator();
if (o == null) {
while (it.hasNext()) {
if (it.next() == null) {
it.remove();
return true;
}
}
} else {
while (it.hasNext()) {
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
public void clear() {
Iterator<E> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
public String toString() {
Iterator<E> it = iterator();
if (!it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (!it.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
}
Key Characteristics #
Optional Operations #
Some operations are optional and may throw UnsupportedOperationException:
add(E e)- for immutable collectionsremove(Object o)- for immutable collectionsclear()- for immutable collections
Fail-Fast Behavior #
Most implementations provide fail-fast iterators that throw ConcurrentModificationException when the collection is modified during iteration.
Thread Safety #
The Collection interface does not guarantee thread safety. Implementations may or may not be thread-safe:
ArrayList,HashSet,HashMap: Not thread-safeVector,Hashtable: Thread-safe (synchronized methods)CopyOnWriteArrayList,ConcurrentHashMap: Thread-safe with better concurrency
Performance Characteristics #
| Operation | Typical Complexity |
|---|---|
size() | O(1) |
isEmpty() | O(1) |
contains(Object) | O(n) for lists, O(1) for hash-based |
add(E) | O(1) amortized for ArrayList, O(1) for LinkedList |
remove(Object) | O(n) for lists, O(1) for hash-based |
iterator() | O(1) |
toArray() | O(n) |
Common Implementations #
List Implementations #
- ArrayList: Resizable array, fast random access
- LinkedList: Doubly-linked list, fast insertions/removals at ends
- Vector: Legacy thread-safe ArrayList
- CopyOnWriteArrayList: Thread-safe list for frequent reads, infrequent writes
Set Implementations #
- HashSet: Hash table implementation, O(1) operations
- LinkedHashSet: Hash table + linked list for insertion order
- TreeSet: Red-black tree for sorted elements
- EnumSet: Specialized set for enum constants
Queue Implementations #
- PriorityQueue: Priority heap implementation
- ArrayDeque: Resizable array deque
- LinkedList: Also implements Queue interface
Best Practices #
- Choose the right implementation: Select based on access patterns and requirements
- Use interface types: Program to
Collection,List,Setinterfaces rather than implementations - Consider thread safety: Use concurrent collections for multi-threaded access
- Use bulk operations:
addAll(),removeAll()are often more efficient than loops - Leverage default methods: Use
stream(),parallelStream(),removeIf()for cleaner code - Be aware of optional operations: Check documentation or use
instanceofchecks
Common Pitfalls #
- ConcurrentModificationException: Modifying collection while iterating
- UnsupportedOperationException: Calling optional operations on immutable collections
- Performance issues: Choosing wrong implementation for access patterns
- Memory leaks: Holding references to large collections unnecessarily
- HashCode/equals contracts: Violating these can break hash-based collections
Evolution of the Interface #
Java 1.2 (1998) #
- Original Collection interface introduced
- Basic operations: size, isEmpty, contains, iterator, toArray
- Modification operations: add, remove
- Bulk operations: containsAll, addAll, removeAll, retainAll, clear
Java 5 (2004) #
- Generics added:
Collection<E> - Enhanced for-loop support via Iterable
Java 8 (2014) #
- Default methods added: stream(), parallelStream(), removeIf()
- Lambda expressions support
Java 9+ (2017+) #
- Factory methods: List.of(), Set.of(), Map.of()
- Collection literals via factory methods
Relationship with Other Interfaces #
Iterable (root)
↓
Collection (basic operations)
↓
List (positional access) Set (no duplicates) Queue (FIFO)
↓
Deque (double-ended queue)
When to Use Collection Interface #
Use the Collection interface when:
- You need basic collection operations without positional access
- You want to write generic code that works with any collection type
- You’re implementing a custom collection class
- You need to pass collections to methods that don’t care about specific implementation
Use more specific interfaces (List, Set, Queue) when you need their specialized operations.