Read with questions
1. What collections do Java have
2. What are the application scenarios of different sets
Which implementation classes are thread-safe
4. Why can’t Java collections store primitive types
5. What are fail-fast and Fail-safe for collections
An overview of Java collections
Java provides developers with a set of interfaces and implementations for Collections, which are Collections of Java objects, through the Java Collections Framework(JCF).
Students who have studied data structure are certainly familiar with the definition of all kinds of collections. Java improves the convenience of development for developers, improves the compatibility of programs and reduces the complexity of programming by providing a series of built-in data structures.
Images from www.pdai.tech/md/java/col…
A Java Collection consists of two top-level interfaces: Collection, which is a Collection that primarily holds objects, and Map, which is a Collection that holds key-value pairs.
Let’s make a brief introduction to the implementation of the two types of collections.
Collection
Collection is an interface introduced in Jdk1.2 to describe a Collection of stored objects. Its extended interfaces include List, Queue, and Set.
List
List accesses elements sequentially in the form of a List, ensuring that elements are inserted in the same order as they are stored.
implementation | Thread safety | |
---|---|---|
ArrayList | An array of | no |
LinkedList | Two-way linked list | no |
CopyOnWriteArrayList | An array of | Yes, use CopyOnWrite to keep the thread safe |
Vector | An array of | Yes, use Synchronized to ensure thread safety |
Stack | An array of | If yes, inherit Vector |
Vector is a thread-safe list implementation introduced in Jdk1.0, which predated the design of Collection interface. Synchronized is directly added to the method to ensure thread-safe. Stack is the Stack structure inherited from Vector implementation. It is not recommended in actual environments.
Queue
A Queue is a first-in, first-out structure, with elements added from the end of the Queue and ejected from the head. The subinterface Deque is a double-endian queue, that is, both ends can enter and exit.
implementation | Thread safety | |
---|---|---|
ArrayDeque | Circular array | no |
LinkedList | The list | no |
PriorityQueue | The heap | no |
BlockingQueue | BlockingQueue is the extension interface for blocking queues | is |
Because BlockingQueue is a bit more complex and involves some more advanced scenarios, I’ll leave it to you later.
Set
A Set is a collection of non-repeating elements that do not contain the same elements and generally do not maintain the order in which they are inserted
implementation | Thread safety | |
---|---|---|
HashSet | Hash table | No, based on HashMap |
TreeSet | Red and black tree | No, it is implemented based on TreeMap |
LinkedHashSet | Hash table + linked list | If no, implement it based on LinkedHashMap |
CopyOnWriteArraySet | An array of | Yes, CopyOnWrite guarantees |
ConcurrentSkipListSet | Jump table | Yes, implemented based on ConcurrentSkipListMap |
Map
Map is used to store <Key, Value> key-value pairs.
implementation | Thread safety | |
---|---|---|
HashMap | Hash table plus red black tree | no |
TreeMap | Red and black tree | no |
LinkedHashMap | Hash table + linked list + red black tree | no |
WeakHashMap | Hash table | no |
ConcurrentHashMap | Hash table plus red black tree | Yes, it is implemented based on CAS and Synchronized |
ConcurrentSkipListMap | Jump table | is |
Why can’t Java collections hold primitive types
Java introduced the JCF framework in version 1.2 and Java generics in version 1.5, so collections used Object as their storage type by default before generics were introduced.
Take List for example. List List =new ArrayList();
list.add(123); / / automatic boxing
list.add("123");
int num = (int) list.get(0);
String str = (String) list.get(1);
Copy the code
It is obvious that there are defects in this method. Any element based on Object can be put into the collection, but the acquirer of the element cannot determine the specific type of the element, which is prone to type conversion errors. After the introduction of generics in version 1.5, the collection interface was standardized and generic parameters were added. Java’s generics mechanism still essentially erases concrete types as Object, so when a generic collection is initialized, it cannot specify parameters as primitive types that are not derived from Object.
What are fail-fast and Fail-safe
List<String> list = new ArrayList();
list.add("123");
list.add("456");
//(1) throw ConcurrentModificationException
for (String s : list) {
list.remove(s);
}
//(2) Normal removal
Iterator<String> it = list.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
//(3) throw ConcurrentModificationException
new Thread(() -> {
for (String s : list) {
System.out.println(s);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ignore)
{
}
}
}).start();
new Thread(() -> {list.add("789"); }).start();Copy the code
The code above, (1) (3) will throw ConcurrentModificationException, (2) can remove all elements properly. First learn about ConcurrentModificationException, when to modify an object to make concurrent not allowed, will throw this exception.
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
So (1) it’s a single thread, and (3) the element is added concurrently without modifying the existing element.
ArrayList var1 = new ArrayList();
var1.add("123");
var1.add("456");
Iterator var2 = var1.iterator();
while(var2.hasNext()) {
String var3 = (String)var2.next();
var1.remove(var3);
}
Copy the code
A decompilation of the class file of code (1) shows that foreach is actually iterating through the Iterator, and removing the Iterator is done directly by calling list.remove. Let’s dive into the list.iterator method again.
/** Returns an iterator over the elements in this list in proper sequence. * The returned iterator is fail-fast. */
public Iterator<E> iterator(a) {
return new Itr();
}
private class Itr implements Iterator<E> {...intexpectedModCount = modCount; .final void checkForComodification(a) {
if(modCount ! = expectedModCount)throw newConcurrentModificationException(); }}Copy the code
Iterator method will create a Itr object, when its a copy modCount to expectedModCount, each iteration will determine when the two values are the same, if different throw ConcurrentModificationException.
ModCount is a member variable derived from AbstractList that counts the number of times the list has been modified. ModCount is incremented every time add/remove is called.
// The number of times this list has been structurally modified.
protected transient int modCount = 0;
Copy the code
The problem is obvious. ModCount changes every time a list is modified. The iterator in foreach records the modCount value at the time the iterator is created. Cause modCount! ExpectedModCount = expectedModCount, so an exception is thrown. (3) The code is the same problem and will not be repeated.
* <p><a name="fail-fast">
* The iterators returned by this class's {@link #iterator() iterator} and
* {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
* if the list is structurally modified at any time after the iterator is
* created, in any way except through the iterator's own * {@link ListIterator#remove() remove} or * {@link ListIterator#add(Object) add} methods, the iterator will throw a * {@link ConcurrentModificationException}. Thus, in the face of * concurrent modification, the iterator fails quickly and cleanly, rather * than risking arbitrary, non-deterministic behavior at an undetermined * time in the future.Copy the code
In all Java collection classes, all collections directly under java.util except Vector, Stack, and HashTable are fail-fast, while collections under java.util. Concurrent are fail-safe. That is, a collection of concurrent traversal and modification, the implementation of which is guaranteed by the respective thread safety mechanism.
Why fail-fast
Fail-fast means to fail quickly. In a non-thread-safe collection application, concurrent additions or deletions to a collection can cause another thread traversing the collection to have unknown errors, such as a number of groups out of bounds. Therefore, non-thread-safe collection implementations introduce Fail-fast as a way to quickly interrupt threads without causing unknown interlocking problems.
reference
- Java collection framework in detail
- Fail-fast vs. Fail-safe