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>)实现原理也是如上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值