抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

小结

  • Collection 接口的接口 对象的集合(单列集合)
    • List 接口:元素按进入先后有序保存,可重复
      • LinkedList:链表, 插入删除, 没有同步, 线程不安全
      • ArrayList:数组, 随机访问, 没有同步, 线程不安全
      • Vector :数组, 同步, 线程安全
        • Stack:是Vector类的实现类
    • Set 接口: 仅接收一次,不可重复,并做内部排序
      • HashSet:使用hash表(数组)存储元素
      • LinkedHashSet:链表维护元素的插入次序
      • TreeSet:底层实现为二叉树,元素排好序
  • Map 接口:键值对的集合 (双列集合)
    • Hashtable:同步, 线程安全
    • HashMap:没有同步, 线程不安全
    • LinkedHashMap:双向链表和哈希表实现
    • WeakHashMap
    • TreeMap:红黑树对所有的key进行排序
    • IdentifyHashMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static <T extends AbstractCollection> void setInfo(T s) {setInfo(s,null);}
private static <T extends AbstractCollection> void setInfo(T s, String info) {
String typeName = s.getClass().getTypeName().split("\\.")[2];
System.out.println("+======" + typeName + "=======================");
System.out.print("| " + typeName + "源数据:");
for (int i = 0; i < 10; i++) {
Random random = new Random();
int i1 = random.nextInt(10);
if (info != null && !info.isEmpty()) {
System.out.print(info + i1 + ",");
s.add(info + i1);
} else {
System.out.print(i1 + ",");
s.add(i1);
}
try {
if (i == 7){
s.add(null);
}
}catch (Exception e){
System.out.print("[尝试写入null数据,出现异常:e = " + e.getClass().getTypeName().split("\\.")[2]+"],");
}

}
System.out.println();
System.out.print("| " + typeName + "处理后:");
s.forEach(a -> System.out.print(a + ","));
System.out.println();
System.out.println("+========================================");
}

List

校验array和linked的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private static <T extends AbstractList> T setList(T t, int num) {
for (int i = 0; i < num; i++) {
Random random = new Random();
int i1 = random.nextInt(10);
t.add(i1);
}
return t;
}

private static <T extends AbstractList> T addOne(T t, int e) {
t.add(50000,e);
return t;
}

private static <T extends AbstractList> T removeOne(T t, int index) {
t.remove(index);
return t;
}
private static <T extends AbstractList> void getOne(T t, int index){
t.get(index);
}
private static <T extends AbstractList> T consume(T t, String a, int num) {
String typeName = t.getClass().getTypeName().split("\\.")[2];
if (a != null && a.equals("a")) {
long startTime = System.currentTimeMillis();
addOne(t, num);
System.out.println(typeName + ":addOne:总计耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
if (a != null && a.equals("r")) {
long startTime = System.currentTimeMillis();
removeOne(t, num);
System.out.println(typeName + ":removeOne:总计耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
if (a != null && a.equals("g")) {
long startTime = System.currentTimeMillis();
getOne(t, num);
System.out.println(typeName + ":getOne:总计耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
if (a == null) {
long startTime = System.currentTimeMillis();
setList(t, num);
System.out.println(typeName + ":setList:总计耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
return t;
}
1
2
3
4
5
6
7
8
9
10
11
12
public void testList() {
ArrayList<Integer> arrayList = new ArrayList<>();
ArrayList<Integer> list = consume(arrayList, null, 10_000_000);
consume(list, "a", 5);
consume(list, "r", 5_000);
consume(list, "g", 1000_000);
LinkedList<Integer> linkedList = new LinkedList<>();
LinkedList<Integer> list1 = consume(linkedList, null, 10_000_000);
consume(list1, "a", 5);
consume(list1, "r", 5_000);
consume(list1, "g", 1000_000);
}
1
2
3
4
5
6
7
8
ArrayList:setList:总计耗时:629ms
ArrayList:addOne:总计耗时:4ms
ArrayList:removeOne:总计耗时:4ms
ArrayList:getOne:总计耗时:0ms
LinkedList:setList:总计耗时:1901ms
LinkedList:addOne:总计耗时:1ms
LinkedList:removeOne:总计耗时:0ms
LinkedList:getOne:总计耗时:3ms

校验null值

1
2
3
4
5
6
public void testList1() {
ArrayList<Integer> arrayList = new ArrayList<>();
setInfo(arrayList);
LinkedList<Integer> linkedList = new LinkedList<>();
setInfo(linkedList);
}
1
2
3
4
5
6
7
8
+======ArrayList=======================
| ArrayList源数据:4,5,1,1,8,9,8,2,2,8,
| ArrayList处理后:4,5,1,1,8,9,8,2,null,2,8,
+========================================
+======LinkedList=======================
| LinkedList源数据:7,6,4,8,0,3,5,9,4,2,
| LinkedList处理后:7,6,4,8,0,3,5,9,null,4,2,
+========================================

校验线程安全问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void testSafe() {
ArrayList<Integer> arrayList = new ArrayList<>();
inThread(arrayList, 3, 1000);

LinkedList<Integer> linkedList = new LinkedList<>();
inThread(linkedList, 3, 1000);

}

private static <T extends AbstractList> void inThread(T t, int tNum, int eNum) {
Thread thread = new Thread(() -> {
for (int i = 0; i < tNum; i++) {
Thread thread1 = new Thread(() -> setList(t, eNum));
thread1.start();
}
});
thread.start();
try {
thread.join();
Thread.sleep(eNum);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(
// MessageFormat.format("在{0}个线程,每线程添加{1}个元素的情况下,{2}中元素数量是:{3}",
// tNum,eNum,t.getClass().getTypeName().split("\\.")[2],t.size()));
System.out.println(
String.format("在%1$s个线程,每线程添加%2$s个元素的情况下,%3$s中元素数量是:%4$s",
tNum,eNum,t.getClass().getTypeName().split("\\.")[2],t.size()));
}

// 在3个线程,每线程添加1000个元素的情况下,ArrayList中元素数量是:1242
// 在3个线程,每线程添加1000个元素的情况下,LinkedList中元素数量是:2887

list总结

  • 数据可重复,可为null

  • ArrayListLinkedList区别

    1. 两者都不保证线程安全
    2. ArrayList查询快,增删慢LinkedList查询慢,增删快
  • 上述代码中:为什么两者同样是插入10_000_000条数据,LinkedList要比ArrayList慢这么多?

    这是因为setList方法中用来添加元素的方式是尾插法, 参考可以了解到,在大数据量的情况下,ArrayList的扩容机制同LinkedListNode机制要更有优势。

  • 为什么线程不安全呢?

    ArrayList不安全的原因:

    • 首先明确ArrayList添加元素的操作是在add元素时,通过size下标进行写入集合,这一过程中用到了size++操作,这是无法保证原子性的操作,也就导致ArrayList在多线程环境下,可能会触发数组下标越界的错误ArrayIndexOutOfBoundsException,或者size同时被多个线程读取的问题。

    LinkdedList不安全的原因:

    • 首先明确LinkedList API中添加元素方法有多种,这里就用linkLast方法为例。该方法涉及变量尾指针``Node last,下一个指针 Node newNode,大小size,以及修改次数modCount。方法内size++modCount++这两个操作无法保证原子性,导致在多线程环境下,存在多个同时获取相同的sizemodCount`数值的线程,从而导致尾指针出现异常情况。
  • 如何保证线程安全呢?

    1
    2
    Collection<Integer> synchronizedCollection = 
    Collections.synchronizedCollection(new ArrayList<Integer>());
    1
    CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();

    方式1:属于java.util.Collections,通过synchronized实现线程安全。直接锁集合对象,性能不太行。

    1
    2
    3
    4
    5
    SynchronizedCollection(Collection<E> c) {
    this.c = Objects.requireNonNull(c);
    mutex = this;
    }
    public boolean add(E e) {synchronized (mutex) {return c.add(e);}}

    方式2:属于java.util.concurrent.CopyOnWriteArrayList,诸如add,remove,set中存在着copyOf操作,导致该api在处理大数据量的时候,性能表现极差。下面是源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lock();
    try {
    // 获取当前元素集
    Object[] elements = getArray();
    // 获取长度
    int len = elements.length;
    // 拷贝一份容量比原先大1
    Object[] newElements = Arrays.copyOf(elements, len + 1);
    // 将元素加入到新元素集合
    newElements[len] = e;
    // 将新元素集合写到list中
    setArray(newElements);
    return true;
    } finally {
    lock.unlock();
    }
    }

    两者都没有继承AbstractList

Set

校验特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TreeSet<Integer> treeSet = new TreeSet<>();
setInfo(treeSet,null);

HashSet<Integer> hashSet = new HashSet<>();
setInfo(hashSet, null);

LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
setInfo(linkedHashSet,null);

System.out.println("TreeSet和HashSet的区别");
TreeSet<Integer> treeSet2 = new TreeSet<>();
setInfo(treeSet2, "号");
HashSet<Integer> hashSet2 = new HashSet<>();
setInfo(hashSet2, "号");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+======TreeSet=======================
| TreeSet源数据:6,0,8,6,6,9,6,2,[尝试写入null数据,出现异常:e = NullPointerException],1,3,
| TreeSet处理后:0,1,2,3,6,8,9,
+========================================
+======HashSet=======================
| HashSet源数据:4,0,2,5,5,7,1,7,5,5,
| HashSet处理后:0,null,1,2,4,5,7,
+========================================
+======LinkedHashSet=======================
| LinkedHashSet源数据:4,4,1,7,5,3,1,1,0,3,
| LinkedHashSet处理后:4,1,7,5,3,null,0,
+========================================
TreeSet和HashSet的区别
+======TreeSet=======================
| TreeSet源数据:号1,号5,号5,号4,号4,号5,号6,号0,[尝试写入null数据,出现异常:e = NullPointerException],号7,号0,
| TreeSet处理后:号0,号1,号4,号5,号6,号7,
+========================================
+======HashSet=======================
| HashSet源数据:号8,号6,号9,号2,号1,号5,号0,号6,号8,号8,
| HashSet处理后:号1,null,号2,号0,号5,号6,号9,号8,
+========================================

校验线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static <T extends AbstractSet> void inThread(T t, int tNum, int eNum) {
Thread thread = new Thread(() -> {
for (int i = 0; i < tNum; i++) {
Thread thread1 = new Thread(() -> setSet(t, eNum));
thread1.start();
}
});
thread.start();
try {
thread.join();
Thread.sleep(eNum);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
String.format("在%1$s个线程,每线程添加%2$s个元素的情况下,%3$s中元素数量是:%4$s",
tNum, eNum, t.getClass().getTypeName().split("\\.")[2], t.size()));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public void testSetSafe() {
// Testing whether a TreeSet is thread-safe
TreeSet<Integer> treeSet = new TreeSet<>();
inThread(treeSet, 3, 1000);
// Implementing thread-safe TreeSet
Collection<Integer> synchronizedTreeSet =
Collections.synchronizedCollection(new TreeSet<Integer>());
Thread thread = new Thread(() -> {
for (int i = 0; i < 3; i++) {
Thread thread1 = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
synchronizedTreeSet.add(j);
}
});
thread1.start();
}
});
thread.start();
try {
thread.join();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("synchronizedTreeSet:" + synchronizedTreeSet.size());

// Testing whether a HashSet is thread-safe
HashSet<Integer> hashSet = new HashSet<>();
inThread(hashSet,3,1000);
// Implementing thread-safe HashSet
Collection<Integer> synchronizedHashSet = Collections.synchronizedCollection(new HashSet<>());
Thread thread = new Thread(() -> {
for (int i = 0; i < 3; i++) {
Thread thread1 = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
synchronizedHashSet.add(j);
}
});
thread1.start();
}
});
thread.start();
try {
thread.join();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("synchronizedHashSet:" + synchronizedHashSet.size());

// Testing whether a LinkedHashSet is thread-safe
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
inThread(linkedHashSet,3,1000);
// Implementing thread-safe LinkedHashSet
Collection<Integer> synchronizedLinkedHashSet =
Collections.synchronizedCollection(new LinkedHashSet<>());
Thread thread = new Thread(() -> {
for (int i = 0; i < 3; i++) {
Thread thread1 = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
synchronizedLinkedHashSet.add(j);
}
});
thread1.start();
}
});
thread.start();
try {
thread.join();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("synchronizedLinkedHashSet:" + synchronizedLinkedHashSet.size());
}

结果

1
2
3
4
5
6
7
8
9
java.lang.NullPointerException
在3个线程,每线程添加1000个元素的情况下,TreeSet中元素数量是:1005
synchronizedTreeSet::1000

在3个线程,每线程添加1000个元素的情况下,HashSet中元素数量是:1110
synchronizedHashSet:1000

在3个线程,每线程添加1000个元素的情况下,LinkedHashSet中元素数量是:1055
synchronizedLinkedHashSet:1000

set总结

  • 保证数据是唯一的

  • TreeSetHashSetLinkedHashSet三者的区别

    1. TreeSet不能存储null数据HashSetLinkedHashSet可以存储null数据
    2. TreeSet可以保证数据有序HashSet可以保证重写hashcode的数据类型有序LinkedHashSet可以保留插入顺序
    3. TreeSet底层是二叉树HashSet底层是hashLinkedHashSet底层是链表hash
  • TreeSet调了一个NavigableMap,其插入操作会去调用TreeMap中的put方法。多线程中,在调用put时,可能遇到多个线程在添加同一个元素的情况出现,这使compare的结果一致,从而触发插入多个同一元素的情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private transient NavigableMap<E,Object> m;

    private static final Object PRESENT = new Object();

    TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
    }

    public TreeSet() {
    this(new TreeMap<E,Object>());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
    compare(key, key); // type (and possibly null) check

    root = new Entry<>(key, value, null);
    size = 1;
    modCount++;
    return null;
    }
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
    do {
    parent = t;
    cmp = cpr.compare(key, t.key);
    if (cmp < 0)
    t = t.left;
    else if (cmp > 0)
    t = t.right;
    else
    return t.setValue(value);
    } while (t != null);
    }
    else {
    if (key == null)
    throw new NullPointerException();
    @SuppressWarnings("unchecked")
    Comparable<? super K> k = (Comparable<? super K>) key;
    do {
    parent = t;
    cmp = k.compareTo(t.key);
    if (cmp < 0)
    t = t.left;
    else if (cmp > 0)
    t = t.right;
    else
    return t.setValue(value);
    } while (t != null);
    }
    Entry<K,V> e = new Entry<>(key, value, parent);
    if (cmp < 0)
    parent.left = e;
    else
    parent.right = e;
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
    }
  • HashSet内部调了个HashMap,其插入操作会去调用HashMap中的put方法。多线程中,在调用put时,可能遇到多个线程在添加同一个元素的情况出现,这使对比hash的结果一致,从而触发插入多个同一元素的情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
    }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
    else {
    Node<K,V> e; K k;
    if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    e = p;
    else if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    else {
    for (int binCount = 0; ; ++binCount) {
    if ((e = p.next) == null) {
    p.next = newNode(hash, key, value, null);
    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    treeifyBin(tab, hash);
    break;
    }
    if (e.hash == hash &&
    ((k = e.key) == key || (key != null && key.equals(k))))
    break;
    p = e;
    }
    }
    if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
    e.value = value;
    afterNodeAccess(e);
    return oldValue;
    }
    }
    ++modCount;
    if (++size > threshold)
    resize();
    afterNodeInsertion(evict);
    return null;
    }
  • LinkedHashSet是链表结构的HashSet,调的都是HashMap

Map

校验特性

Hashtable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hashtable<Integer, Integer> hashtable = new Hashtable<>();
try {
hashtable.put(1,1);
hashtable.put(1,2);
hashtable.put(3,3);
hashtable.put(4,null);
hashtable.put(null,2);
}catch (Exception e){
System.out.println("e = " + e);
}
hashtable.keySet().forEach((key)->{
System.out.print("key = " + key);
System.out.println(",value = "+ hashtable.get(key));
});

// e = java.lang.NullPointerException
// key = 3,value = 3
// key = 1,value = 2

结果:

  • keyvalue都不能为空

  • key覆盖

  • 不建议使用HashTable,Oracle官方也将其废弃,建议在多线程环境下使用ConcurrentHashMap类。

  • 线程安全

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    Hashtable<Integer, Integer> hashtable = new Hashtable<>();
    inThread(hashtable,3,1000);
    private static <T extends Dictionary> void inThread(T t, int tNum, int eNum) {
    Thread thread = new Thread(() -> {
    for (int i = 0; i < tNum; i++) {
    Thread thread1 = new Thread(() -> setMap(t, eNum));
    thread1.start();
    }
    });
    thread.start();
    try {
    thread.join();
    Thread.sleep(eNum);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(
    String.format("在%1$s个线程,每线程添加%2$s个元素的情况下,%3$s中元素数量是:%4$s",
    tNum, eNum, t.getClass().getTypeName().split("\\.")[2], t.size()));
    }

    private static <T extends Dictionary> T setMap(T t, int num) {
    for (int i = 0; i < num; i++) {
    t.put(i, i);
    }
    return t;
    }

    // 在3个线程,每线程添加1000个元素的情况下,Hashtable中元素数量是:1000

HashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
HashMap<Integer, Integer> hashMap = new HashMap<>();
try{
hashMap.put(1,1);
hashMap.put(1,2);
hashMap.put(3,3);
hashMap.put(4,null);
hashMap.put(null,2);
}catch (Exception e){
System.out.println("e = " + e);
}
hashMap.keySet().forEach((key)->{
System.out.print("key = " + key);
System.out.println(",value = "+ hashMap.get(key));
});
//key = null,value = 2
//key = 1,value = 2
//key = 3,value = 3
//key = 4,value = null

HashMap<Integer, Integer> hashMap = new HashMap<>();
inThread(hashMap,3,1000);
//在3个线程,每线程添加1000个元素的情况下,HashMap中元素数量是:1020

结果:

  • 允许keyvaluenull
  • 线程不安全

LinkedMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>();
try{
linkedHashMap.put(1,1);
linkedHashMap.put(1,2);
linkedHashMap.put(3,3);
linkedHashMap.put(4,null);
linkedHashMap.put(null,2);
}catch (Exception e){
System.out.println("e = " + e);
}
linkedHashMap.keySet().forEach(key->{
System.out.print("key = " + key);
System.out.println(",value = "+ linkedHashMap.get(key));
});

//key = 1,value = 2
//key = 3,value = 3
//key = 4,value = null
//key = null,value = 2
LinkedHashMap<Integer, Integer> linkedHashMap =
new LinkedHashMap<>();
inThread(linkedHashMap,3,1000);
// 在3个线程,每线程添加1000个元素的情况下,LinkedHashMap中元素数量是:1157

结果:

  • 源码,其继承自HashMap

    1
    public class LinkedHashMap<K,V>    extends HashMap<K,V>
  • 线程不安全

TreeMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
TreeMap<Integer, Integer> treeMap = new TreeMap<>();
try {
treeMap.put(1, 1);
treeMap.put(1, 2);
treeMap.put(3, 3);
treeMap.put(4, null);
treeMap.put(null, 2);
} catch (Exception e) {
System.out.println("e = " + e);
}
treeMap.keySet().forEach((key) -> {
System.out.print("key = " + key);
System.out.println(",value = " + treeMap.get(key));
});
//e = java.lang.NullPointerException
//key = 1,value = 2
//key = 3,value = 3
//key = 4,value = null

TreeMap<Integer, Integer> treeMap = new TreeMap<>();
inThread(treeMap,3,1000);
// NullPointerException
// 在3个线程,每线程添加1000个元素的情况下,TreeMap中元素数量是:1016

结果:

  • 不允许keynullvalue可以为null
  • key覆盖
  • 线程不安全

ConcurrentHashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ConcurrentHashMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<>();
try {
concurrentHashMap.put(1, 1);
concurrentHashMap.put(1, 2);
concurrentHashMap.put(3, 3);
concurrentHashMap.put(4, null);
concurrentHashMap.put(null, 2);
} catch (Exception e) {
System.out.println("e = " + e);
}
concurrentHashMap.keySet().forEach((key) -> {
System.out.print("key = " + key);
System.out.println(",value = " + concurrentHashMap.get(key));
});
// e = java.lang.NullPointerException
// key = 1,value = 2
// key = 3,value = 3

ConcurrentHashMap<Integer, Integer> concurrentHashMap =
new ConcurrentHashMap<>();
inThread(concurrentHashMap,3,1000);
//在3个线程,每线程添加1000个元素的情况下,concurrent中元素数量是:1000

结果:

  • 属于java.util.concurrent,但继承了AbstractMap
  • 不允许key,valuenull
  • 线程安全

检验线程安全代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static <T extends AbstractMap> void inThread(T t, int tNum, int eNum) {
Thread thread = new Thread(() -> {
for (int i = 0; i < tNum; i++) {
Thread thread1 = new Thread(() -> setMap(t, eNum));
thread1.start();
}
});
thread.start();
try {
thread.join();
Thread.sleep(eNum);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
String.format("在%1$s个线程,每线程添加%2$s个元素的情况下,%3$s中元素数量是:%4$s",
tNum, eNum, t.getClass().getTypeName().split("\\.")[2], t.size()));
}
private static <T extends AbstractMap> T setMap(T t, int num) {
for (int i = 0; i < num; i++) {
t.put(i, i);
}
return t;
}

java.util.concurrent

集合类在java.util,为解决并发问题,提供了java.util.concurrent解决方案。比如常用的HashMap并发解决方案的ConcurrentHashMap就在这个包下。更多内容多线程-concurrent中介绍。

Collections

collection操作的工具类。

摘要几个常用的API

方法 描述
sort(List<T> list [, Comparator<? super T> c]) list进行排序;重载一个比较器版本
synchronizedCollection(Collection<T> c)
synchronizedList(List<T> list)
synchronizedMap(Map<K,V> map)
synchronizedSet(Set<T> set)
提供并发解决方案
min/max (Collection<T> c [, Comparator<? super T> c]) 获取最小/最大值
reverse(List<T> list) 反转列表
replaceAll(List<T> list,T oldVal,T newVal) 替换列表中元素
swap(List<T> list,int i,int j) 列表中元素交换位置
copy(List<? super T> dest, List<? extends T> src) srcdest拷贝,确保src.sizedest.size

评论