Java的集合有两个根接口:Collection接口、Map接口

1.Collection接口下的子接口与子类:

Collection接口常用方法:

 

 

1.1 Set接口

TreeSet:基于红黑树实现,支持有序操作。

树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。 

在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

性质1. 结点是红色或黑色。

性质2. 根结点是黑色。

性质3. 所有叶子都是黑色。(叶子是NIL结点)

性质4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

性质5. 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。

红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。

使用TreeSet存储对象,这个对象的类要实现 Comparable接口,并重写CompareTo()方法。或者在调用TreeSet构造函数的时候,传入一个实现了Comparator接口的比较器。

TreeSet底层使用TreeMap的key来实现。

 

HashSet:基于哈希表实现,支持快速查找,但不支持有序操作。

hashSet底层使用“数组 + 链表/红黑树”的数据结构存储。

判断元素重复依靠两个方法:1、hashCode()  2、equals()。

HashSet底层使用的是HashMap,具体来说是使用的HashMap的key来存储的。

 

LinkedHashSet:具有HashSet的查找效率,并且内部使用双向链表维护元素的插入顺序。

 

1.2 List接口

List接口常用方法:

 

 

ArrayList:基于动态数组实现,支持随机访问。

扩容机制:初始化时是空数组,容量为0。当添加第一个元素时,容量扩展到10。当下次扩容时,容量变为原来容量的1.5倍。

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
        
//list转数组
Integer[] aryIntegers = list.toArray(new Integer[0]);
        
//数组转list,但这个list的长度固定,不能添加和删除
List<Integer> nList = Arrays.asList(aryIntegers); 

 

Vector:和ArrayList类似,线程安全。

LinkedList:基于双向链表实现,只能顺序访问。

 

1.3 Queue接口

LinkedList:基于双向链表实现,只能顺序访问。可以用做栈、队列和双向队列。

PriorityQueue:基于堆结构实现,可以用来实现优先队列。

 

2.Map接口下的子接口与子类:

Map接口常用方法:

 

Map<String, Integer> map = new HashMap<String, Integer>();
System.out.println(map.size());
map.put("a", 1);//将对象存入集合中,key重复则覆盖原值
map.get("a");//根据健获取对应的值
map.keySet();//返回所有key
map.values();//返回包含所有值的Collection集合
map.entrySet();//健值匹配的Set集合

 

TreeMap:基于红黑树实现。

 

HashMap:基于哈希表实现。线程不安全,允许null作为key和value。

新创建的map容量为0,当添加第一个元素时,默认容量16。

加载因子0.75,表示当元素数达到当前最大容量的75%时,进行扩容。每次扩容容量变为之前的2倍(左移1位)。

使用数组 + 链表/红黑树 实现。

如果链表的高度到8,数组的长度超过64,则将链表转化为红黑树;长度低于6,则将红黑树转为链表。(JDK1.8+)

JDK1.8之前是头插法,JDK1.8之后是尾插法。

 

HashTable:和HashMap类似,线程安全。已过时,建议使用ConcurrentHashMap代替。不允许使用null作为key和value。

 

LinkedHashMap:使用双向链表维护元素顺序,顺序为插入顺序或LRU顺序。

 

Collections工具列提供的方法:

 

 

线程不安全集合转成线程安全集合的方法:

 

 

 

3.线程安全的集合:

 

3.1 CopyOnWriteArrayList:

线程安全,写有锁,读无锁,读写之间不阻塞,性能优于读写锁。写入时先copy一份副本,再添加新元素,最后替换引用。

 1 //演示CopyOnWriteArrayList的使用方式
 2 public class demo01 {
 3     public static void main(String[] args) {
 4         CopyOnWriteArrayList<String> list= new CopyOnWriteArrayList<String>();
 5         ExecutorService eService = Executors.newFixedThreadPool(5);
 6         for(int i=0;i<5;i++) {
 7             eService.submit(new Runnable() {                
 8                 @Override
 9                 public void run() {
10                     for(int j=0;j<10;j++) {
11                         list.add(Thread.currentThread().getName()+"|"+new Random().nextInt(100));                        
12                     }                    
13                 }
14             });
15         }
16         eService.shutdown();
17         while(!eService.isTerminated()) {            
18         }
19         
20         for(String str:list) {
21             System.out.println(str);
22         }
23         System.out.println(list.size());
24     }    
25 }

 

3.2CopyOnWriteArraySet:

线程安全的Set,底层使用CopyOnWriteArrayList实现(有序)。使用addIfAbsent()添加元素,会遍历数组。

如存在元素,则不添加(扔掉副本)。

 1 //CopyOnWriteArraySet的使用
 2 public class demo02 {
 3     public static void main(String[] args) {
 4         CopyOnWriteArraySet<String> copyOnWriteArraySet = new CopyOnWriteArraySet<String>();
 5         copyOnWriteArraySet.add("a");
 6         copyOnWriteArraySet.add("a");
 7         copyOnWriteArraySet.add("b");
 8         copyOnWriteArraySet.add("c");
 9         
10         System.out.println("元素个数:"+copyOnWriteArraySet.size());
11         System.out.println(copyOnWriteArraySet.toString());        
12     }
13 }

 

3.3Queue接口:

常用方法:

抛出异常的有:

boolean add(E e); //顺序添加一个元素(达到上限后再添加会报异常)

E remove(); //获得第一个元素并移除(队列没有元素,调用会报异常)

E element(); //获得第一个元素但不移除(队列没有元素,则会报异常)

 

返回特殊值:(推荐使用)

boolean offer(E e); //顺序添加一个元素(达到上限后再添加会返回false)

E poll(); //获得第一个元素并移除(队列没有元素,调用会返回null)

E peek(); //获得第一个元素但不移除(队列没有元素,则返回null)

 

3.4ConcurrentLinkedQueue:

线程安全、可高效读写的队列,高并发下性能最好的队列。

无锁、使用CAS算法。由硬件支持,效率高。

CAS算法原理:

V:要更新的变量

E:预期值

N新值

先从V复制值到E,在更新时,判断V是否与E相等,如果V不等于E,则表示原值V被修改了,则取消当前操作。

 

3.5BlockingQueue接口:

阻塞队列,增加了两个方法,线程状态为无限期等待。

void put(E e); //将指定元素插入此队列中,如果没有可用空间,则等待。

E take(); //获取并移除此队列头部元素,如果没有可用元素,则等待。

可用于解决“生产者、消费者”问题。

实现类:

ArrayBlockingQueue,数组结构实现,有界队列(手动指定数组大小)

LinkedBlockingQueue,链表结构实现,有界队列(默认上限Integer.MAX_VALUE)

 

下面的代码使用ArrayBlockingQueue实现“生产者、消费者”问题:

 1 public class demo03 {
 2     public static void main(String[] args) {
 3         ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<>(6);
 4         
 5         Runnable runnable1 = new Runnable() {            
 6             @Override
 7             public void run() {
 8                 for(int i=0;i<20;i++) {
 9                     try {
10                         arrayBlockingQueue.put(i);
11                         System.out.println("生产了:"+i);
12                     } catch (InterruptedException e) {
13                         // TODO Auto-generated catch block
14                         e.printStackTrace();
15                     }
16                 }                
17             }
18         };
19         
20         Runnable runnable2 = new Runnable() {            
21             @Override
22             public void run() {
23                 for(int i=0;i<20;i++) {
24                     try {
25                         Integer num = arrayBlockingQueue.take();
26                         System.out.println("消费了:"+num);
27                     } catch (InterruptedException e) {
28                         // TODO Auto-generated catch block
29                         e.printStackTrace();
30                     }
31                 }                
32             }
33         };
34         
35         new Thread(runnable1,"生产者").start();
36         new Thread(runnable2,"消费者").start();
37     }
38 }

 

 

4.线程安全的Map实现类:

 

4.1ConcurrentHashMap:

在JDK1.8之前,使用分段锁设计。

初始容量默认为16段(Segment),。

每一段都是一个独立的Hash表。

不对整个Map加锁,而实为每个Segment单独加锁。

当多个对象存入同一个Segment时,才需要互斥。

最理想状态为16个对象分别存入16个Segment,并行数量16.

使用方式与HashMap无异。

 

在JDK1.8之后,使用CAS算法来实现。

posted on 2021-01-26 13:10  Sempron2800+  阅读(61)  评论(0编辑  收藏  举报