概念区分: java.util.Collection和java.util.Collections区别:
1、 java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
2、java.util.Collections 是一个包装类(工具类/帮助类)。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,用于对集合中元素进行排序、搜索以及线程安全等各种操作,服务于Java的Collection框架。
既然Collections是服务于Collection框架的,那么我们就尝试学习使用:
方法1:
java.util.Collections.unmodifiableCollection(Collection<? extends Double>): 返回指定 collection 的不可修改视图。
package com.daxin.collections;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class Main1 {
public static void main(String[] args) {
Collection<Double> list = new ArrayList<Double>();
double array[] = { 75, 52, 99, 102, 78,23 };
System.out.println("list.hashCode()1 "+list.hashCode());
for (int i = 0; i < array.length; i++) {
list.add(array[i]);
}
System.out.println("list.hashCode()2 "+list.hashCode());
//返回不可以修改视图
Collection<Double> unmodifiableCollection = Collections.unmodifiableCollection(list);
unmodifiableCollection.add(1.2);//抛异常
for (Double d : unmodifiableCollection) {
System.out.println(d);
}
}
}
当我们对返回的不可修改视图进行调用unmodifiableCollection.add(1.2)修改时候,会抛出异常:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1075)
at com.daxin.collections.Main1.main(Main1.java:24)
那究竟是如何实现不可以修改的呢? 答案:使用的代理模式,具体过程看源码如下:
返回的不可修改视图类型为:java.util.Collections.UnmodifiableCollection<E>,是Collections的一个静态内部类,而unmodifiableCollection方法实现如下:
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
return new UnmodifiableCollection<>(c);
}
其中c是 UnmodifiableCollection类中的一个成员:
final Collection<? extends E> c;
此处是不是会有疑问?c是final的,但是内部成员应该可以修改?为什么?
拓展补充:首先ArrayList的实现底层就是使用的数组:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private transient Object[] elementData;
.
.
.
等
}
所以对于 final Collection<? extends E> c中finalc为什么不可修修改感觉疑问呢?其实此处使用代理模式是对add方法进行了增强,看一下add方法就知道了:
public boolean add(E e) {
throw new UnsupportedOperationException();
}
答案自然见分晓了吧!
但是需要注意的是,如果我们自己在代码中修改了原始的那个list,那么unmodifiableCollection也会被修改,这里的视图类似数据库的视图。
接下来看看方法:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
返回一个同步的Map。返回由指定映射支持的同步(线程安全的)映射。为了保证按顺序访问,必须通过返回的映射完成所有对底层实现映射的访问。
首先看一下非线程安全的HashMap:
package com.daxin.collections;
import java.util.HashMap;
import java.util.Map;
class Service {
private Map map = null;
public Service(Map map) {
super();
this.map = map;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
service.getMap().put(i + "", i + "");
}
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
service.getMap().put(i + "", i + "AAA");
}
}
}
public class Main2 {
public static void main(String[] args) throws Exception {
Map map=new HashMap<String,String>();
Service service = new Service(map);
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.start();
b.start();
a.join();
b.join();
System.out.println(service.getMap());
// 输出{3=3AAA, 2=2AAA, 1=1AAA, 5=5AAA, 4=4AAA} 或者{3=3AAA, 2=2AAA, 1=1,
// 5=5AAA, 4=4AAA} 多次运行结果不一致,两个线程交替执行导致结果多次输出不一致。出现了线程安全问题
//线程安全的请看Main3源码
}
}
package com.daxin.collections;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class Main3 {
public static void main(String[] args) throws Exception {
Map map=Collections.synchronizedMap(new HashMap<String,String>());
Service service = new Service(map);
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.start();
b.start();
a.join();
b.join();
System.out.println(service.getMap());
//输出:{3=3AAA, 2=2AAA, 1=1AAA, 5=5AAA, 4=4AAA}或者{3=3, 2=2, 1=1, 5=5, 4=4} 两种结果了,这就主要看
//谁先得到锁了谁后得到锁了,输出的是最后得到锁的结果。
//不会两个线程交替执行了
}
}
接下来看看synchronizedMap的方法源码:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
再看看SynchronizedMap构造方法(SynchronizedMap是Collections的一个静态内部类):
private static class SynchronizedMap<K, V> implements Map<K, V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K, V> m; // Backing Map
final Object mutex; // Object on which to synchronize,同步加锁对象
SynchronizedMap(Map<K,V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
mutex = this;//将mutex设置为当前对象,还有其他构造函数自行查看
}
...等
}
再看看SynchronizedMap的put等常用方法源码:
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized (mutex) {return m.remove(key);}
}
public void putAll(Map<? extends K, ? extends V> map) {
synchronized (mutex) {m.putAll(map);}
}
每一次对SynchronizedMap进行操作其实都是对mutex对象加锁进行的,使用的是synchronized代码块实现。
在看看java.util.Collections.synchronizedSet(Set<String>)方法,原理类似:
创建一个SynchronizedSet返回(SynchronizedSet继承SynchronizedCollection类,SynchronizedCollection中实现对成员mutex的成员进行代码块同步)。
方法java.util.Collections.synchronizedList(List<String>)实现原理也是如上。