Understanding java.util.Collection Interface
This article explains the java.util.Collection by providing a high-level overview of the Java Collections Framework and interacting with elements.
Join the DZone community and get the full member experience.
Join For FreeWhen we talk about Java collections, we consider two things. First, the more wide concept means implementations of common data structures. And another, narrower understanding, corresponds to concrete interfaces and their implementations, that are contained in Java Collections Framework, supplied out-of-the box. In that sense, we need to remember that such collections inherit to the root interface – java.util.Collection
, which – in its regard – defines all common functionality, that sub classes should implement.
Such common manipulations include various operations with elements, such as insertion and deletion, as well iterations or connection with Java Streams API. That is why I tried to put all these important concepts of java.util.Collection
in one post, as they are crucial to know for any Java developer.
Which Data Structures Are Collections
This section provides a high-level view on Java Collections Framework. It offers for developers a unified architecture for representing and manipulating collections, enabling collections to be manipulated independently of implementation details. Java defines a collection as an object that represents a group of objects. So, Java Collections Framework (JCF) includes a number of interfaces and implementations that facilitate data operations like searching, sorting, insertion, manipulation, or deletion of elements.
Take a look on the graph below:
One of common interview questions sounds like “why do we use JCF”? Well, there is a number of reasons, why JCF is so useful and important:
- JCF reduces programming efforts, because you don’t need to reinvent these data structures and algorithms yourself
- JCF offers high-performance implementations of data structures and algorithms, therefore it increases performance
- JCF establishes interoperability between unrelated APIs
- JCF makes it easier to learn collections
The root classes here are java.util.Collection
and java.util.Map
. That is very important to remember, as it is very common to think that Map is a collection. While maps contain collection-view operations, which enable them to be manipulated as collections, from a technical point of view, maps are not collections in Java.
As it was already mentioned, Java collection is a group of objects, that are known as its elements. Note, that elements in some collections have to be unique (for example, sets), while other types permit duplicates (for example, array-based lists). java.util.Collection
is not implemented directly, rather Java has implementations of its subinterfaces. This interface is typically used to pass collections around and manipulate them with max degree of generality.
Add Elements to Collections
There are two ways to insert new elements to collection:
- Add a single element
- Add all elements from the another collection.
Note, that both of these methods are marked optional. That meands, that concrete implementations are permitted to not perform one or more of these operations (in such cases they throw UnsupportedOperationException
when insertion is performed).
Take a look on the code snippet below:
x
void insertTest(){
// get mock posts
List<Post> posts = getPosts();
// Add single element
Post post = new Post(6, "Phasellus scelerisque", "Phasellus scelerisque eros id lacus auctor");
posts.add(post);
assertThat(posts).contains(post).hasSize(6);
// add multiple elements
List<Post> newPosts = new ArrayList<>();
newPosts.addAll(posts);
assertThat(newPosts).containsAll(posts);
}
In examples I use an ArrayList
implementation that is one of the Java collections and which implements both add()
and addAll()
methods. ArrayList has two add()
methods, but we concentrate here on the Collection’s one.
To sum up, java.util.Collection
permits us to insert elements in these ways:
boolean add(Element e)
= this method adds a new element and ensures collection contains the specified element. So it returns either true or false depending if this collection changed as a result of the call.boolean addAll(Collection c)
= this method inserts all elements from thec
to the collection. Note, that this method also returns boolean value, which stands true if this collection changed as a result of the call
Several implementations impose restrictions on elements that they may contain, and as a result, they prohibit certain insertions. For example, if collection does not allow duplicates, you can’t add an element that already exists. The same goes for null
values.
Delete an Element
Compare to two inserting methods, there are more methods to remove elements from the collection. Take a look on them:
void clear()
= removes all of the elements from the collectionboolean remove(Element e)
= removes a single instance of the specified elemente
from the collection (if that element is presented)boolean removeAll(Collection c)
= deletes all of the collection’s elements that are also contained in the argument’s collectionboolean removeIf(Predicate filter)
= deletes all of the elements of this collection that satisfy the given predicate.
Let have a look on the example below:
x
void removeTest(){
// get mock posts
List<Post> posts = getPosts();
Post post = posts.get(2);
assertThat(posts).contains(post);
// remove object
posts.remove(post);
assertThat(posts).doesNotContain(post);
// clear
posts.clear();
assertThat(posts).isEmpty();
// delete with predicate
posts = getPosts();
// remove posts with ID 2 and 4
posts.removeIf(p -> p.getId() % 2 == 0);
assertThat(posts).hasSize(3);
}
It is important to remember that not all of these methods are optional (means, can be skipped in particular implementations). removeIf
method is not optional operation, while remove
, removeAll
and clear
are optional operations.
Work with Streams API
In Java stream stands for a sequence of elements supporting sequential and parallel aggregate operations. java.util.Collection
has two methods to initialize streams (both are not optional):
Stream<E> stream()
= creates a sequential stream with this collection as its sourceStream<E> parallelStream()
= creates a possibly parallel stream with this collection as its source.
Let have a look on the code snippet below:
x
void streamTest(){
List<Post> posts = getPosts();
Stream<Post> stream = posts.stream();
assertThat(stream).isInstanceOf(Stream.class);
}
Iteration
From a technical point of view, iterations mean a technique used to sequence through a block of code repeatedly until a specific condition either exists or no longer exists. Java provides us several approaches to iterate over a collection. Note, that not all collections provide us way to access an element on a base of index (for instance, sets do not). Therefore in this section we will not explore popular iteration approaches, which will not work for each collection. Rather we will concentrate on approaches, that can be used with any collection.
As Java collections are also Iterable
let explore how it permits us to go through elements of collection:
- Using iterators
- Using streams
- using
forEach
x
void iterationTest(){
List<Post> posts = getPosts();
// create iterator
Iterator<Post> iterator = posts.iterator();
// Option 1 with hasNext
System.out.println("Iteration using iterator hasNext");
while(iterator.hasNext()){
Post post = iterator.next();
System.out.println(post);
}
Iterator<Post> iterator2 = posts.iterator();
// Option 2 using forEachRemaining
System.out.println("Iteration using iterator forEachRemaining");
iterator2.forEachRemaining(p -> System.out.println(p));
// using forEach
System.out.println("Iteration using forEach");
posts.forEach(System.out::println);
// using stream
System.out.println("Iteration using stream");
posts.stream().forEach(p -> System.out.println(p));
}
Basically, the iterator pattern permits all elements of the collection to be accessed sequentially, with some operation being performed on each element. You can note that iterator has two core methods:
hasNext()
– this method returns true if the iteration has more elements and we use it in the while loop (likenext()
in ResultSet)next()
– returns an element and we use it to access the current element of iteration
You can also use forEachRemaining
method. It accepts a Consumer
function that is executed for each remaining element until all elements have been processed or the action throws an exception.
Note, that iterator()
method is not optional.
Another approaches to mention here could be forEach
method and using Streams API.
Access Individual Element of The Collection
I have to say here that java.util.Collection
does not contain methods to access individual elements. That means, thet each concerete implementation has its own ways. For instance elements of array-based lists can be accessed by their index, while sets do not support that. It is very important to remember that there is no way to access Collection’s elements, as it depends on its subsclasses.
Other Non-Optional Methods
We did not provided detailed explanations to these methods, however, they are still important to know. I group them under this section:
contains(Element e)
= returns true if this collection contains the specified element. Usesequals()
of object in order to check an equality of the element.containsAll(Collection c)
= returns true if this collection contains all of the elements in the specified collection.isEmpty()
= returns true if the collection is emptysize()
= gets an integer value with a number of elements in the collectiontoArray()
= creates an arrayElement[]
from the elements of the collection
That what stands for all collections. Of course, each particular type has different underlying implementation of these methods, due to concrete data structure’s logic. But all of them inherit non-optional functionality from the root interface, that as we said already, ensures better learning curve for developers and promotes interoperability.
I tried to accumulate all important concepts in this post. If you think that something is missed or you have questions regarding java.util.Collection
and concrete types, don’t hesitate to leave a comment below or contact me directly using these channels.
Published at DZone with permission of Yuri Mednikov. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
A Comprehensive Guide To Testing and Debugging AWS Lambda Functions
-
SRE vs. DevOps
-
Introduction to API Gateway in Microservices Architecture
-
Deploying Smart Contract on Ethereum Blockchain
Comments