源码(码云):https://gitee.com/yin_zhipeng/to_study_the_collection.git |
---|
- 简易的:文件位置simple_collection/uml/collection-simple.puml

声明 :看源码时经常看见这个变量modCount,就是标记容器被操作的次数的,在这里统一说一下,否则遇见一次说一次,想一头撞死 |
---|
文章目录
一、Collection接口
Collectiono 是顶级接口,我们对其常用Api进行介绍 |
---|
1. 常用Api解析
描述 | API |
---|
添加单个元素 | add(E e) |
添加一个集合 | addAll(Collection<? extends E> c) |
清除集合 | clear() |
移除指定元素 | remove(Object o) |
迭代器 | iterator() |
集合元素个数 | size() |
判断指定元素是否在集合中 | contains(Object o) |
比较两个集合的元素是否相等 | equals(Object o) |
集合是否为空 | isEmpty() |
测试API:simple_collection/CollectionAPITest.java |
---|
import java.util.*;
public class CollectionAPITest {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(1);
col.add(2);
System.out.println("使用add方法添加1和2:"+col);
List<Integer> integers = Arrays.asList(new Integer[]{3, 4, 5, 6, 7, 8});
col.addAll(integers);
System.out.println("使用addAll方法添加{3, 4, 5, 6, 7, 8}:"+col);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("使用remove()方法移除元素1:"+col.remove(1)+"(true成功/false失败)移除后集合"+col);
System.out.println("再次使用remove()方法移除元素1:"+col.remove(1)+"(true成功/false失败)移除后集合"+col);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
col.clear();
System.out.println("使用clear方法清除集合"+col);
System.out.println("使用isEmpty方法查看集合是否为空"+col.isEmpty());
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
ArrayList<Object> objects = new ArrayList<>();
System.out.println("集合一:"+col+"集合二:"+objects+"使用equals()比较两个集合元素是否相等"+col.equals(objects));
objects.add(1);
System.out.println("集合一:"+col+"集合二:"+objects+"使用equals()比较两个集合元素是否相等"+col.equals(objects));
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("集合:"+objects+"使用contains()方法判断集合是否包含1这个元素"+objects.contains(1));
System.out.println("集合:"+objects+"使用contains()方法判断集合是否包含2这个元素"+objects.contains(2));
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("集合:"+col+"使用size()方法获取集合长度"+col.size());
System.out.println("集合:"+objects+"使用size()方法获取集合长度"+objects.size());
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
col.addAll( Arrays.asList(new Integer[]{3, 4, 5, 6, 7, 8}));
System.out.println("使用iterator()遍历集合"+col);
Iterator iterator = col.iterator();
int i = 1;
while(iterator.hasNext()){
Object next = iterator.next();
System.out.println("第"+i+"个元素:"+next);
i++;
}
}
}
2. Iterator接口
首先Collection extends Iterable,而Iterable接口中定义了Iterator iterator();,返回Iterator接口对象,我们操作迭代的方法,都在Iterator接口 |
---|
接口是不可用的,如果想用需要实现类,我们从ArrayList源码中,可以看到,实现类就是Itr类,Itr类是内部类 |
---|
3. ArrayList的iterator()方法源码(JDK1.8)
4. 集合之间如何比较
1. Comparable 和 Comparator
- Comparabel接口(内部比较器),实现更简单, Comparator接口(外部比较器)更灵活
import java.util.*;
public class Test implements Comparable<Test>{
private Integer age;
public Test(Integer age) {
this.age = age;
}
@Override
public int compareTo(Test o) {
return this.age-o.age;
}
@Override
public String toString() {
return "Test{" +
"age=" + age +
'}';
}
public static void main(String[] args) {
Test test = new Test(1);
Test test1 = new Test(2);
ArrayList<Test> tests = new ArrayList<>();
tests.add(test1);
tests.add(test);
System.out.println("list集合排序前"+Arrays.toString(tests.toArray())+"\n\n");
System.out.println("==========Comparable排序===========");
int i = test.compareTo(test1);
System.out.println("Comparable:test和test1谁大(正数表示test大,0表示相等,负数表示test1大)"+i);
Collections.sort(tests);
System.out.println("list集合排序后"+Arrays.toString(tests.toArray())+"\n\n");
System.out.println("==========Comparator排序===========");
Comparator<Test> comparator = new Comparator<Test>() {
@Override
public int compare(Test o1, Test o2) {
return o2.age-o1.age;
}
};
int compare = comparator.compare(test, test1);
System.out.println("Comparator:test和test1谁大(正数表示test1大,0表示相等,负数表示test大)"+compare);
Collections.sort(tests, new Comparator<Test>() {
@Override
public int compare(Test o1, Test o2) {
return o2.age-o1.age;
}
});
System.out.println("list集合排序后"+Arrays.toString(tests.toArray()));
}
}
二、List接口
List也是一个接口,继承于Collection,Collection有的他都有,所以我们介绍它特有的常用Api进行介绍 |
---|
1. 常用Api解析
listIterator()和listInterator(int index)等方法,放在后面统一介绍,放在这里有的突兀 |
---|
描述 | API |
---|
添加 单个元素到指定下标 | add(int index,E e) |
设置(替换) 单个元素到指定下标 | set(int index,E e) |
获取指定下标元素 | get(int index) |
移除指定下标元素 | remove(int index) |
测试API:simple_collection/ListApiTest.java |
---|
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ListApiTest {
public static void main(String[] args) {
List list = new ArrayList();
list.addAll(Arrays.asList(new Integer[]{4,7,8,-1}));
System.out.println("List init ====>>> "+list);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
list.add(1,5);
System.out.println("List add(1,5):在下标为1的位置添加元素5"+list);
list.set(1, 6);
System.out.println("List set(1,6):在下标为1的位置设置元素6"+list);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("List get(1):获取下标为1的元素:"+list.get(1));
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
list.remove(1);
System.out.println("List remove(1):移除下标为1的元素:"+list);
}
}
2. ArrayList源码(JDK1.8)
简单说一下JDK1.7版本的细节,具体源码看JDK1.8的 |
---|
3. Vector源码(JDK1.8)
4. iterator()方法的Itr迭代器并发修改异常
ArrayList是线程不安全的,使用迭代器的时候,不可以同时对集合进行其它操作,比如用迭代器添加数据,一个指针迭代,另一个指针操作集合,就会报错 |
---|
那么我们可以让一个人做这件事,解决并发修改异常,使用ListIterator接口 |
---|
5. ListIterator接口,ArrayList的ListItr实现类
ListIterator接口继承于Iterator接口,同时ArrayList也使用内部类ListItr实现了此接口,ListItr内部类还继承了Itr内部类 |
---|
遍历和添加,都使用ListIterator对象,不报并发修改异常 |
---|
遍历时还可以判断上一个元素,以完成逆向遍历,前提是,当前ListIterator指针已经在靠后的位置(不等于0) |
---|
三、LinkedList实现类
1. 常用Api解析
描述 | API |
---|
插入指定元素到列表开头 | addFirst(E e) |
插入指定元素到列表结尾 | addLast(E e) |
获取但不移除此列表的头 | E element() |
获取但不移除此列表的头 | E peek() |
获取但不移除此列表的第一个元素,列表为空,返回null | E peekFirst() |
获取但不移除此列表的最后一个元素,列表为空,返回null | E peekLast() |
返回此列表第一个元素 | getFirst() |
返回此列表最后一个元素 | getLast() |
返回此列表首次出现的指定元素的索引,无元素返回-1 | indexOf(Object o) |
返回此列表最后出现的指定元素的索引,无元素返回-1 | lastIndexOf(Object o) |
获取并移除此列表的头 | poll() |
获取并移除此列表的第一个元素,列表为空,返回null | pollFirst() |
获取并移除此列表的最后一个元素,列表为空,返回null | pollLast() |
从此列表所表示的堆栈处弹出一个元素 | pop() |
将元素推入此列表所表示的堆栈 | push(E e) |
将指定元素添加到此列表的末尾 | offer(E e) |
将指定元素插入到此列表的开头 | offerFirst(E e) |
将指定元素插入到此列表的末尾 | offerLast(E e) |
移除并返回此列表的第一个元素,列表为空,报错 | E removeFirst() |
移除并返回此列表的最后一个元素 | E removeLast() |
测试API:simple_collection/LinkedListApiTest.java |
---|
import java.util.LinkedList;
public class LinkedListApiTest {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
System.out.println("addFirst()插入指定元素到列表开头");linkedList.addFirst("addFirst()");
System.out.println("addLast()插入指定元素到列表结尾");linkedList.addLast("addLast()");
System.out.println("push()入栈");linkedList.push("push()");
System.out.println("offer()元素添加到末尾");linkedList.offer("offer()");
System.out.println("offerFirst()元素插入到列表开头");linkedList.offerFirst("offerFirst()");
System.out.println("offerLast()元素插入到列表末尾");linkedList.offerLast("offerLast()");
linkedList.push("push()");
System.out.println("添加元素完成:"+linkedList);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("element():获取但不移除列表头"+linkedList.element());
System.out.println("peek():获取但不移除列表头"+linkedList.peek());
System.out.println("peekFirst():获取但不移除列表头,列表空,返回null:"+linkedList.peekFirst());
System.out.println("peekLast():获取但不移除列表尾,列表空,返回null:"+linkedList.peekLast());
System.out.println("getFirst():列表第一个元素"+linkedList.getFirst());
System.out.println("getLast():列表最后一个元素"+linkedList.getLast());
System.out.println("linkedList.indexOf(push()):获取列表第一个'push()'的下标"+linkedList.indexOf("push()"));
System.out.println("lastIndexOf(push()):获取列表最后一个'push()'的下标"+linkedList.lastIndexOf("push()"));
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("pop(),出栈弹出一个元素=="+linkedList.pop()+"==移除后列表:"+linkedList);
System.out.println("poll(),获取并移除此列表的头=="+linkedList.poll()+"==移除后列表:"+linkedList);
System.out.println("pollFirst(),获取并移除列表首元素,列表空,返回null:=="+linkedList.pollFirst()+"==移除后列表:"+linkedList);
System.out.println("pollLast(),获取并移除列表尾元素,列表空,返回null:=="+linkedList.pollLast()+"==移除后列表:"+linkedList);
System.out.println("removeFirst(),移除并返回此列表第一个元素=="+linkedList.removeFirst()+"==移除后列表:"+linkedList);
System.out.println("removeLast(),移除并返回此列表最后一个元素=="+linkedList.removeLast()+"==移除后列表:"+linkedList);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("遍历======");
for(Iterator iterator =linkedList.iterator();iterator.hasNext();){
System.out.println(iterator.next());
}
}
}
2. 两种迭代器使用方法比较
Iterator iterator = col.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
for(Iterator iterator =linkedList.iterator();iterator.hasNext();){
System.out.println(iterator.next());
}
3. LinkedList源码(JDK1.8)
四、Set接口
Set接口继承于Collection接口,Collection有的他都有,特性如下 |
---|
Set接口的API和List接口大致相同,但是少了和索引相关的API,Set中没有索引 ,所以我们不介绍特有API |
---|
Set底层都是用Map实现,所以我们研究源码时,只看怎么用的Map,具体原理请看后面Map的源码 |
---|
1. HashSet实现类和源码(JDK1.8)
基本特性测试:simple_collection/HashSetApiTest.java |
---|
import java.util.HashSet;
import java.util.Set;
public class SetApiTest {
public static void main(String[] args) {
Set set = new HashSet();
set.add(1);set.add(1);set.add(1);set.add(1);set.add(1);set.add(1);
System.out.println("向set集合中添加6个元素1,根据数据唯一特性,集合中将只有1个1:"+set);
set.add("a");set.add("b");set.add(2);set.add(3);set.add(4);
set.add(5);set.add(6);set.add(7);set.add("asdfsdf");set.add("asdasdffsdf");set.add("asdfasdfasdfsdf");
System.out.println("Set集合无序测试,查看是否和插入顺序一至:" +set);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("测试添加集合中没有的值,添加元素10:"+set.add(10));
System.out.println("测试添加集合中已经有值,添加元素1:"+set.add(1));
}
}
2. LinkedHashSet实现类和源码(JDK1.8)
LinkedHashSet 和 HashSet的区别 |
---|
测试,发现有序了:simple_collection/LinkedHashSetTest.java |
---|
import java.util.LinkedHashSet;
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
set.add(1);set.add(1);set.add(1);set.add(1);set.add(1);set.add(1);
System.out.println("向set集合中添加6个元素1,根据数据唯一特性,集合中将只有1个1:"+set);
set.add("a");set.add("b");set.add(2);set.add(3);set.add(4);
set.add(5);set.add(6);set.add(7);set.add("asdfsdf");set.add("asdasdffsdf");set.add("asdfasdfasdfsdf");
System.out.println("Set集合无序测试,查看是否和插入顺序一至:" +set);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("测试添加集合中没有的值,添加元素10:"+set.add(10));
System.out.println("测试添加集合中已经有值,添加元素1:"+set.add(1));
}
}
3. TreeSet实现类和源码(JDK1.8)
和HashSet基本一样,只是底层是用二叉树实现的(遍历方式为中序遍历),一样的不按插入顺序排序,一样的不允许重复,但是会排序 :simple_collection/TreeSetTest.java |
---|
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet();
set.add(1);set.add(1);set.add(1);set.add(1);set.add(1);set.add(1);
System.out.println("向set集合中添加6个元素1,根据数据唯一特性,集合中将只有1个1:"+set);
set.add(9);set.add(13);set.add(7);set.add(16);set.add(17);set.add(15);
System.out.println("TreeSet集合使用:内部比较器排序:" +set);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("测试添加集合中没有的值,添加元素10:"+set.add(10));
System.out.println("测试添加集合中已经有值,添加元素1:"+set.add(1));
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
TreeSet<Integer> integers = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
integers.add(9);integers.add(13);integers.add(7);integers.add(16);integers.add(17);integers.add(15);
System.out.println("TreeSet集合使用:外部比较器排序:" +integers);
}
}
五、Map接口
Map是一个顶级接口,主要规定采用键值对的形式存储数据,多采用Hash表和二叉树两种数据结构实现,接口不能new对象,我们用HashMap介绍Map的Api |
---|
1. 常用Api解析
描述 | API |
---|
从此映射中移除所有隐射关系 | clear() |
如果此映射包含指定键的映射关系,返回true | boolean containsKey(Object key) |
如果此映射将一个或多个键映射到指定值,返回true | boolean containsValue(Object value) |
返回此映射中包含的映射关系的Set视图 | Set<Map.Entry<K,V>> entrySet() |
比较指定的对象与此映射是否相等 | boolean equals(Object o) |
返回指定键所映射的值;如果此映射不包含该键的映射关系,返回null | V get(Object key) |
返回此映射的哈希码值 | int hashCode() |
如果此映射未包含键-值映射关系,返回true | boolean isEmpty() |
返回此映射包含的键的Set视图 | Set< K > keySet() |
将指定的值与此映射中的指定键关联(可选操作) | V put(K key,V value) |
从指定映射中将所有映射关系复制到此映射中(可选操作) | void putAll(Map<? extends K,? extends V> m) |
如果存在一个键的映射关系,则将其从此映射中移除(可选操作) | V remove(Object key) |
返回此映射中的键-值映射关系数 | int size() |
返回此映射中包含的值的Collection视图 | Collection< V > values() |
测试API:simple_collection/MapApiTest.java |
---|
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapApiTest {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
System.out.println("测试一下对同一个key赋值,put()方法的返回值");
System.out.println("put(\"one\", 3)返回值:"+map.put("one", 3));
System.out.println("put(\"one\", 2)返回值:"+map.put("one", 2));
System.out.println("put(\"one\", 1)返回值:"+map.put("one", 1));
System.out.println("对同一个key,one设置3个值,只会保留最后一个,一个键映射一个值,重复赋值会替换:"+map);
map.put("two",2);
map.put("three",3);
map.put("four",4);
System.out.println("map集合初始化完成(会按照key的hash排序):"+map);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("通过get('one')获取one对应的值:"+map.get("one"));
System.out.println("当前map的大小:"+map.size());
System.out.println("keySet()获取所有key,Set<K>对象:"+map.keySet());
System.out.println("values()获取所有映射值Collection对象:"+map.values());
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("map1.equals(map2):判断两个map的映射关系,是否一致:"+map.equals(map));
System.out.println("isEmpty()判断集合是否为空:"+map.isEmpty());
System.out.println("map.containsKey(\"one\"):判断集合中是否包含key值one的映射:"+map.containsKey("one"));
System.out.println("map.containsValue(1):判断1这个值,是否和map中某个或某些key映射:"+map.containsValue(1));
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
System.out.println("remove(\"one\"):删除指定key,one,返回它映射的值(key丢失,1这个值没有引用指向,也会被GC回收):"+map.remove("one"));
System.out.println("remove(\"two\", 3):删除指定key-value对,不满足映射关系,不删除:"+map.remove("two", 3));
System.out.println("删除后map:"+map);
map.clear();
System.out.println("map.clear()清空map:"+map);
System.out.println(Integer.toBinaryString(2)+" "+Integer.toBinaryString(-2)+" "+Integer.toHexString(-2>>>2)+" "+Integer.toBinaryString(-2>>>2));
map.put("one", 1);map.put("two",2);map.put("three",3);map.put("four",4);
System.out.print("配合keySet()方法遍历key值:===>>[");
Set<String> keySet = map.keySet();
for(String s:keySet){
System.out.print(s+", ");
}
System.out.println("]");
System.out.print("配合keySet()和get()方法遍历key和value:===>>{");
for(String s:keySet){
System.out.print(s+"="+map.get(s)+", ");
}
System.out.println("}");
System.out.print("配合values()方法遍历value值:===>>[");
Collection<Integer> values = map.values();
for(Integer i : values){
System.out.print(i+", ");
}
System.out.println("]");
System.out.print("配合entrySet()方法,同时遍历key和vlaue值:===>>{");
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
System.out.print(entry.getKey()+"="+entry.getValue()+", ");
}
System.out.println("}");
}
}
2. HashMap源码(JDK1.8)
HashMap底层是由哈希表数据结构实现(数组+链表+红黑树JDK1.8) |
---|
hash不一样,但是下标算成一样的怎么办(哈希碰撞),一定要搞懂,否则后面源码看不懂 |
---|
int h;
String key = "one";
h = key.hashCode();
System.out.println("key通过hashCode()方法获取的hashcode值"+Integer.toBinaryString(h));
int i = h >>> 16;
System.out.println("将hashcode值,右位移16位,用来减少碰撞"+Integer.toBinaryString(i));
int hashCode = (h) ^ (i);
System.out.println("右位移16位异或减少碰撞,还能保留高位和低位的特性:"+hashCode);
System.out.println("hashCode的二进制"+Integer.toBinaryString(hashCode));
int n = 16;
int i1 = 16 - 1;
System.out.println(Integer.toBinaryString(i1));
System.out.println("最终下标位置:"+(i1&hashCode));
源码分析2:如何在用户指定大小时(用户可能指定偶数也可能指定负数,不可以指定就不用这么麻烦了,每次都<<1就可以了),依旧保证大小为2的幂次方,tableSizeFor 表示生成一个大于等于 cap 且为 2 的 N 次方的最小整数 |
---|
System.out.println("=======================测试当我们向要将散列表长度扩充为8,我们如何确保长度为2的幂次方的基础上,还能不浪费空间呢===============================");
int n = 8;
int n2 = n-1;
System.out.println(n+"==================="+n2);
System.out.println(Integer.toBinaryString(n)+"=============="+Integer.toBinaryString(n2));
System.out.println(Integer.toBinaryString(n |= n >>> 1)+"=============="+Integer.toBinaryString(n2|= n2 >>> 1));
System.out.println(Integer.toBinaryString(n |= n >>> 2)+"=============="+Integer.toBinaryString(n2|= n2 >>> 2));
System.out.println(Integer.toBinaryString(n |= n >>> 4)+"=============="+Integer.toBinaryString(n2|= n2 >>> 4));
System.out.println(Integer.toBinaryString(n |= n >>> 8)+"=============="+Integer.toBinaryString(n2|= n2 >>> 8));
System.out.println(Integer.toBinaryString(n |= n >>> 16)+"=============="+Integer.toBinaryString(n2|= n2 >>> 16));
System.out.println((n + 1)+"=============="+(n2+1));
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)
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
3. TreeMap源码(JDK1.8)
TreeMap底层就是一棵二叉排序树,学会二叉排序树就行了,没啥好讲的,就走一遍源码流程得了 |
---|
六、同步类容器
1. Collections工具类转换
首先,集合有一个工具类,Collections,提供一些api,方便我们操作集合,但是不常用。然而我们可以用它将线程不安全的容器,转换成线程安全的。 |
---|
ArrayList<Integer> arrayList = new ArrayList<>();
List<Integer> synchronizedList = Collections.synchronizedList(arrayList);
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CollectionsTest {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
HashMap<String, Integer> hashMap = new HashMap<>();
List<Integer> synchronizedList = Collections.synchronizedList(arrayList);
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(hashMap);
ExecutorService es = Executors.newFixedThreadPool(100);
for(Integer i = 0;i<10000;i++){
Integer finalI = i;
es.execute(new Runnable() {
@Override
public void run() {
synchronizedList.add(finalI);
}
});
}
System.out.println(synchronizedList);
es.shutdown();
while(true){
System.out.println("正在监控线程是否都执行完毕");
if(es.isTerminated()){
System.out.println("所有子线程都执行完毕了!");
System.out.println(synchronizedList);
System.out.println(synchronizedList.size());
if(synchronizedList.size() == 10000){
System.out.println("线程安全!");
}else{
System.out.println("线程不安全!!!");
}
break;
}
}
}
}
2. Collections源码(JDK1.8)
七、并发类容器
JDK1.5之后,提供多种并发类容器,可以代替同步类容器,提升性能、吞吐量 |
---|
1. ConcurrentMap
测试一下,ConcurrentMap最快,HashTable慢了4倍,synchronizedMap最慢:simple_collection/ConcurrentMapApiTest.java |
---|
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentMapApiTest {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> cHMap = new ConcurrentHashMap<>();
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0;i<10;i++){
es.execute(new Runnable() {
@Override
public void run() {
long startTime = System.currentTimeMillis();
for (int j = 0;j<1000000;j++){
cHMap.put("test"+j,j);
}
long endTime = System.currentTimeMillis();
System.out.println(Thread.currentThread()+"==ConcurrentHashMap==>"+"执行完成共需要"+(endTime - startTime));
}
});
}
es.shutdown();
System.out.println("========================HashTable=============================");
ExecutorService es2 = Executors.newFixedThreadPool(10);
Hashtable<String, Integer> hashtable = new Hashtable<>();
for (int i = 0;i<10;i++){
es2.execute(new Runnable() {
@Override
public void run() {
long startTime = System.currentTimeMillis();
for (int j = 0;j<1000000;j++){
hashtable.put("test"+j,j);
}
long endTime = System.currentTimeMillis();
System.out.println(Thread.currentThread()+"==HashTable==>"+"执行完成共需要"+(endTime - startTime));
}
});
}
es2.shutdown();
System.out.println("========================synchronizedMap=============================");
ExecutorService es3 = Executors.newFixedThreadPool(10);
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
for (int i = 0;i<10;i++){
es3.execute(new Runnable() {
@Override
public void run() {
long startTime = System.currentTimeMillis();
for (int j = 0;j<1000000;j++){
synchronizedMap.put("test"+j,j);
}
long endTime = System.currentTimeMillis();
System.out.println(Thread.currentThread()+"==synchronizedMap==>"+"执行完成共需要"+(endTime - startTime));
}
});
}
es3.shutdown();
}
}
2. COW(Copy On Write)并发容器,写时复制容器
1. CopyOnWriteArrayList实现类
简单使用:simple_collection/COWApiTest.java |
---|
import java.util.concurrent.CopyOnWriteArrayList;
public class COWApiTest {
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> cOWList = new CopyOnWriteArrayList<>();
cOWList.add(1);
cOWList.add(1);
cOWList.add(1);
System.out.println("使用add添加,元素可以重复:"+cOWList);
cOWList.addIfAbsent(1);
System.out.println("使用addIfAbsent添加,如果集合中已有相同的,不可以添加:"+cOWList);
}
}
2. CopyOnWriteArrayList源码(JDK1.8)
可重入锁 |
---|
就是一个线程执行一个加锁的代码,此时这个线程,执行另一个加锁的代码,两个锁发现是同一个线程,就让线程可以进入,这就是可重入锁 |
addIfAbsent()方法效率会比add()低很多,因为每次都需要比较 |
---|
3. CopyOnWriteArraySet源码(JDK1.8)
使用和普通set没有区别,不做api介绍,直接上源码分析,其实就是用CopyOnWriteArrayList实现而已,感觉完全没必要看 |
---|
八、队列
简单类图(烦的不行,东西怎么越写越多?):simple_collection/uml/Queue.puml |
---|
- 当从队列取数据时,队列为空
1. BlockingQueue接口常用API解析
BlockingQueue是个接口,需要具体实现类,给出APi,不做测试了 |
---|
描述(🍅:不阻塞/🏆:阻塞) | API |
---|
🍅将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回true,如果没有可用空间,抛出IllegalStateException异常 | boolean add(E e) |
🍅将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回true,如果没有可用空间,返回false | boolean offer(E e) |
🏆将指定元素插入此队列中,将等待可用的空间(如果有必要) | void put(E e) |
🏆获取并移除此队列头部,在元素变得可用之前一直等待(如果有必要) | E take() |
🏆获取并移除此队列的头部,在指定的等待时间前等待可用元素(如果有必要) | E poll(Long timeout,TimeUnit unit) |
🍅从此队列中移除指定元素的单个实例(如果存在) | boolean remove(Object o) |
🍅无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的附加元素数量;如果没有内部限制,返回Integer.MAX_VALUE | int remainingCapacity() |
2. ArrayBlockingQueue实现类和源码(JDK1.8)
import java.util.concurrent.ArrayBlockingQueue;
public class QueueTest {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<Integer>(3);
arrayBlockingQueue.offer(1);
arrayBlockingQueue.offer(2);
arrayBlockingQueue.offer(3);
System.out.println("==================="+arrayBlockingQueue+"===================================");
System.out.println("peek()方法,ArrayBlockingQueue特有,不移除,只是返回值"+arrayBlockingQueue.peek());
System.out.println("take()方法,获取并移除队列头"+arrayBlockingQueue.take());
System.out.println("remove()方法,移除3这个元素"+arrayBlockingQueue.remove(3));
System.out.println("==================="+arrayBlockingQueue+"===================================");
}
}
- dequeue():拿到数组,根据取元素下标获取数据,然后下标位置,置为null,如果下标++超出队列大小,重新置为0;队列元素个数–;迭代器如果不为空,通知迭代器执行逻辑(当队列为空时调用。通知所有活动迭代器队列为空,清除所有弱引用,并解除itrs数据结构的链接。当takeIndex绕到0时调用。通知所有迭代器,并删除任何过时的迭代器)。通知队列满放元素等待池,可以继续放元素了。

- 阻塞
上面的put和take()方法,while是必须的(不能是if),因为如果池子中线程被激活瞬间,其它线程又放入/取出数据,让队列又满了/空了,那么还沿着await后面执行就会出错了 |
---|
3. LinkedBlockingQueue实现类和源码(JDK1.8)
4. SynchronousQueue队列
SynchronousQueue队列:一种很特殊的队列,方便线程间,高效数据传输 |
---|
此队列容量为0,当插入元素时,必须同时有个线程往外取 |
---|
就是说,当你往这个队列里面插入一个元素,它就拿着这个元素站着(阻塞),直到有个取元素的线程来,它就把元素交给它 |
就是用来同步数据的,也就是线程间交互数据用的一个特殊队列 |
package com.mashibing.juc.c_025;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
public class T08_SynchronusQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> strs = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
strs.put("aaa");
System.out.println(strs.size());
}
}
5. PriorityQueque队列
优先队列(二叉树算法,就是排序);队列取的时候有先后顺序,数据有不同权重 |
---|
import java.util.PriorityQueue;
public class T07_01_PriorityQueque {
public static void main(String[] args) {
PriorityQueue<String> q = new PriorityQueue<>();
q.add("c");
q.add("e");
q.add("a");
q.add("d");
q.add("z");
for (int i = 0; i < 5; i++) {
System.out.println(q.poll());
}
}
}
6. DelayQueue队列
无界阻塞队列:只能存放Delayed对象的队列,所以,当我们想要使用这个队列,我们的类必须实现Delayed接口,重写getDelay和compareTo两个方法 |
---|
7. Deque双端队列
前面都是一端放,一端取;这个是两端都可以放,也都可以取 |
---|