Java之Collections工具类方法使用以及源码分析(一)

概念区分: 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>)实现原理也是如上。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值