Considerations for Using Collections
Collection Empty Check
To check whether all elements inside a collection are empty, use the
isEmpty()
method instead ofsize()==0
method.
This is because the isEmpty()
method provides better readability and has a time complexity of O(1).
While the time complexity of the size()
method for most collections we use is also O(1), there are many collections, particularly those in the java.util.concurrent
package (such as ConcurrentLinkedQueue
, ConcurrentHashMap
, etc.), whose complexity is not O(1).
Below is the source code for the size()
and isEmpty()
methods of ConcurrentHashMap
.
1 |
|
Collection to Map Conversion
When using the
toMap()
method of thejava.util.stream.Collectors
class to convert to aMap
collection, it is essential to note that an NPE (NullPointerException) will be thrown if the value is null.
1 |
|
Let’s first examine the toMap()
method of the java.util.stream.Collectors
class. Inside, it invokes the merge()
method of the Map
interface.
1 |
|
The merge()
method of the Map
interface, shown below, is a default implementation within the interface.
1 |
|
The merge()
method first calls the Objects.requireNonNull()
method to check if the value is null.
1 |
|
Collection Traversal
Avoid performing element
remove/add
operations within a foreach loop. For removing elements, use theIterator
approach instead. If concurrent operations are involved, ensure to synchronize access to theIterator
object.
Upon decompilation, you will discover that the foreach syntax underlyingly depends on Iterator
. However, remove/add
operations directly invoke methods of the collection itself, rather than Iterator
‘s remove/add
methods.
This leads to the Iterator
inexplicably discovering that elements have been remove/add
-ed, resulting in a ConcurrentModificationException
being thrown to alert users of a concurrent modification anomaly. This is known as the fail-fast mechanism in single-threaded scenarios.
Fail-fast mechanism: When multiple threads modify a fail-fast collection, a
ConcurrentModificationException
may be thrown.
Starting from Java 8, you can use the Collection.removeIf()
method to remove elements that satisfy specific conditions.
1 |
|
In addition to directly using Iterator
for traversal operations, you can also:
- Utilize ordinary for loops
- Employ fail-safe collection classes. All classes under the
java.util
package are fail-fast, while those under thejava.util.concurrent
package are fail-safe. - …..
Collection Deduplication
You can leverage the unique property of
Set
elements to quickly deduplicate a collection, avoiding the use ofList
‘scontains()
for traversal deduplication or containment checks.
Here, we illustrate using HashSet
and ArrayList
as examples.
1 |
|
The core difference lies in the implementation of the contains()
method.
The contains()
method of HashSet
relies on the containsKey()
method of HashMap
, with a time complexity close to O(1) (O(1) when no hash collisions occur).
If we insert N elements into the Set, the time complexity is close to O(n).
The contains()
method of ArrayList
involves traversing all elements, resulting in a time complexity close to O(n).
Collection to Array Conversion
When converting a collection to an array, it is essential to use the collection’s
toArray(T[] array)
method, passing in an empty array of exactly the same type and with a length of 0.
The toArray(T[] array)
method takes a generic array as its parameter. If no parameter is passed to the toArray
method, it returns an array of type Object
.
1 |
|
Due to JVM optimization, using new String[0]
as the parameter for the Collection.toArray()
method is now preferred. new String[0]
serves as a template, specifying the type of the returned array. The 0 is to save space, as it merely serves to indicate the type of the returned array.
Array to Collection Conversion
When using the utility class
Arrays.asList()
to convert an array to a collection, avoid using its methods for modifying the collection. Itsadd/remove/clear
methods will throw anUnsupportedOperationException
exception.
Arrays.asList()
is quite common in everyday development, allowing us to convert an array into a List
collection.
The JDK source code explains this method as follows:
1 |
|
1. Arrays.asList()
is a generic method, and the array passed must be an object array, not a primitive type array.
1 |
|
When passing a primitive data type array, the actual parameter received by Arrays.asList()
is not the elements of the array, but the array object itself! In this case, the only element of the List
is the array itself, which explains the code above.
2. Using the collection’s modification methods: add()
, remove()
, clear()
will throw an exception.
1 |
|
Arrays.asList()
does not return a java.util.ArrayList
, but rather an inner class of java.util.Arrays
, which does not implement collection modification methods or override these methods.
1 |
|
This inner class inherits from AbstractList
and implements the RandomAccess
and Serializable
interfaces.
If we take a look at the add/remove/clear
methods of java.util.AbstractList
, we’ll understand why UnsupportedOperationException
is thrown.
1 |
|
So, how do we correctly convert an array to an ArrayList
?
1、Manually implement a utility class.
1 |
|
2、The simplest method.
1 |
|
3、Utilize Java 8’s Stream
(recommended).
1 |
|
4、Use Guava.
For immutable collections, you can use the ImmutableList
class and its of()
and copyOf()
factory methods (parameters cannot be empty).
1 |
|
For mutable collections, you can use the Lists
class and its newArrayList()
factory method.
1 |
|
5、Use Apache Commons Collections.
1 |
|
6、 Use Java 9’s List.of()
method.
1 |
|