容器深入研究
BlockingQueue 将在第21章介绍。ConcurrentMap 接口及其实现ConcurrentHashMap 也是用于多线程机制的,同样会在第21章介绍。CopyOnWriteArrayList 和CopyOnWriteArraySet ,它们也是用于多线程机制的。EnumSet 和EnumMap ,为使用enum 而设计的Set 和Map 的特殊实现,将在第19章中介绍。还有很多的abstract 类,这些类只是部分实现了特定接口的工具。比如你要创建自己的Set,那么并不用从Set 接口开始并实现其中的全部方法,只需从AbstractSet 继承,然后执行一些创建新类必须的工作即可。不过一般类库已经足够强大,通常可以忽略这些abstract 类。 我们来看一下Collections 中的填充方法:
import java.util.*;
public class People {
public static void main (String args[]) throws Exception {
List<String> list1 = Collections.nCopies(5 , "hello" );
System.out .println(list1);
List<String> list2 = new ArrayList<String>(list1);
System.out .println(list2);
Collections.fill(list2, "world" );
System.out .println(list2);
}
}
-------------------
[hello, hello, hello, hello, hello]
[hello, hello, hello, hello, hello]
[world, world, world, world, world]
import java.util.*;
public class Test1 {
public static void main (String args[]) {
MyMap m = MyMap.getInstance();
System.out .println(m);
for (Map.Entry<String, String> entry : m.entrySet()) {
System.out .println(entry.getKey() + ": " + entry.getValue());
entry.setValue("change by miaoch" );
}
for (Map.Entry<String, String> entry : m.entrySet()) {
System.out .println(entry.getKey() + ": " + entry.getValue());
}
}
}
class MyMap extends AbstractMap<String, String> {
private static final String[][] DATA = {
{"hello" , "World" },
{"what is" , "you name" },
{"how are" , "you" },
{"you are" , "welcome" }
};
private MyMap () {}
private static MyMap map;
public synchronized static MyMap getInstance () {
if (map == null ) {
map = new MyMap();
}
return map;
}
public Set<Map.Entry<String, String>> entrySet () {
return new AbstractSet<Map.Entry<String, String>>() {
public Iterator<Map.Entry<String, String>> iterator () {
return new Iterator<Map.Entry<String,String>>() {
int index = -1 ;
private Map.Entry<String,String> entry = new Map.Entry<String,String>() {
public String getKey () {
return DATA[index][0 ];
}
public String getValue () {
return DATA[index][1 ];
}
public String setValue (String value ) {
DATA[index][1 ] = value ;
return value ;
}
};
public boolean hasNext () {
return index < size() - 1 ;
}
public Map.Entry<String, String> next () {
index++;
return entry;
}
};
}
public int size () {
return DATA.length;
}
};
}
}
----------------------------
{hello=World, what is =you name, how are=you, you are=welcome}
hello: World
what is : you name
how are: you
you are: welcome
hello: change by miaoch
what is : change by miaoch
how are: change by miaoch
you are: change by miaoch
集合扩展
由于第十一章我已经讲了很多关于集合的东西,此处就再补充一些十一章没有说到的。
SortedSet
我们之前聊过三种Set ,分别是HashSet 、TreeSet 、LinkedHashSet 。而SortedSet 是Set 的子接口,TreeSet 就是它的实现类。从名字上我们就可以知道,它是一个有序的集合,我们通过一个例子了解一下它的常用方法:
import java.util.*;
public class People {
public static void main (String args[]) throws Exception {
SortedSet<String> set = new TreeSet<String>();
Collections.addAll(set , ("one two three four five "
+ "six seven eight nine ten" ).split("\\s" ));
System.out .println(set );
String low = set .first();
String high = set .last();
System.out .println(low + " " + high);
Iterator<String> it = set .iterator();
for (int i = 0 ; i <= 6 ; i++) {
if (i == 3 ) low = it.next();
else if (i == 6 ) high = it.next();
else it.next();
}
System.out .println(low + " " + high);
System.out .println(set .subSet(low, high));
System.out .println(set .subSet(low, "ninf" ));
System.out .println(set .headSet(high));
System.out .println(set .tailSet(low));
}
}
------------------执行结果
[eight, five, four, nine, one, seven, six, ten, three, two]
eight two
nine six
[nine, one, seven]
[nine]
[eight, five, four, nine, one, seven]
[nine, one, seven, six, ten, three, two]
双向队列
之前讲过LinkedList 、PriorityQueue 可以作为队列的实现类。而Deque 是Queue 的子接口,LinkedList 同样也可以作为双向队列Deque 的实现类(事实上它就是直接实现Deque 的)。
import java.util .*
public class People {
public static void main(String args[]) throws Exception {
Deque<String> queue = new LinkedList<String>()
Collections.addAll (queue, ("1 2 3 4 5 "
+ "6 7 8 9 10" ).split ("\\s" ))
System.out .println (queue)
queue.offer ("11" )
queue.offerLast ("12" )
queue.offerFirst ("0" )
System.out .println (queue)
System.out .println (queue.poll ())
System.out .println (queue.pollFirst ())
System.out .println (queue.pollLast ())
System.out .println (queue)
System.out .println (queue.peek ())
System.out .println (queue.peekFirst ())
System.out .println (queue.peekLast ())
System.out .println (queue)
}
}
---------------执行结果
[1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]
[0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ]
0
1
12
[2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 ]
2
2
11
[2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 ]
Map
标准的Java类库包含了Map 的几种基本实现,包括:HashMap 、TreeMap 、LinkedHashMap 、WeakHashMap 、ConcurrentHashMap 、IdentityHashMap 。它们都有同样的基本接口Map ,但是行为特性不同,表现在效率 、键值对的保存呈现次序 、对象的保存周期 、映射表如何在多线程次序中工作 和判定键等价的策略 等等。Map 是一种非常重要的编程工具。不考虑类库中的Map 实现,我们用最简单的方式来实现Map 以加深对Map 的理解。
import java.util.*;
public class Test {
public static void main (String args[]) {
MMap<String, String> map = new MiaochMap<String, String>(5 );
map.put("1" , "111" );
map.put("2" , "222" );
map.put("3" , "333" );
map.put("4" , "444" );
map.put("5" , "555" );
System.out .println(map.get ("1" ));
System.out .println(map.get ("11" ));
System.out .println(map);
for (Object[] objs : map) {
System.out .println(objs[0 ] + " : " + objs[1 ]);
}
}
}
interface MMap<K, V> extends Iterable<Object[]> {
int size();
V get (K key);
V put(K key, V value );
}
class MiaochMap<K, V> implements MMap<K, V> {
private Object[][] data;
private static final int DEFAULT_LENGTH = 10 ;
private int size;
private int length;
public MiaochMap () {
this (DEFAULT_LENGTH);
}
public MiaochMap (int length) {
data = new Object[length][2 ];
size = 0 ;
this .length = length;
}
public Iterator<Object[]> iterator () {
return new Iterator<Object[]>() {
private int index = 0 ;
public boolean hasNext () {
return index < size;
}
public Object[] next () {
return data[index++];
}
};
}
public int size () {
return size;
}
@SuppressWarnings("unchecked" )
public V get (K key) {
Iterator<Object[]> it = iterator();
while (it.hasNext()) {
Object[] kv = it.next();
if (kv[0 ].equals(key)) {
return (V) kv[1 ];
}
}
return null ;
}
public V put (K key, V value ) {
if (size == length) {
throw new ArrayIndexOutOfBoundsException();
}
data[size++] = new Object[]{key, value };
return value ;
}
public String toString () {
StringBuilder result = new StringBuilder("[" );
Iterator<Object[]> it = iterator();
while (it.hasNext()) {
Object[] kv = it.next();
result.append(kv[0 ]);
result.append(" : " );
result.append(kv[1 ]);
result.append(", " );
}
if (result.length() > 2 ) {
result.delete(result.length() - 2 , result.length());
}
result.append("]" );
return result.toString();
}
}
在有了自己对Map 的思考后,我们再来看看几大Map 实现所关注的一些因素。
性能
这个我在第十一章已经讲过了。比如我们实现的这个Map 就有性能的问题。例如我们现在定义了一个10000 大小的Map ,如果我们要get 的key 刚好在最后一个位置,那我们岂不是要从头到尾再走一遍?而HashMap 则使用了散列函数,由key 通过hashCode() 得到一个int值以此来对应一个下标。这样就可以显著提高速度。一般没有特殊要求,我们常用HashMap来做为Map的实现,以下是几大Map的特点:
实现类 备注 HashMap Map基于散列表的实现(取代了Hashtable )。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量 和负载因子 ,以调整容器的性能。 LinkedHashMap 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用 (LRU)的次序。只比HashMap慢一点;而在迭代访问时反而更快,因为它使用链表维护内部次序(HashMap需要先数组再链表重复交替)。 TreeMap 基于红黑树的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparable或Comparator决定)。TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。 WeakHashMap 弱键映射,允许释放映射所指的对象。这是为了解决某类特殊问题而设计的。如果映射之外没有引用指向某个键 ,则此键 可以被垃圾回收器回收。 ConcurrentHashMap 一种线程安全的Map,它不涉及同步加锁。并发章节讨论 IdentityHashMap 使用==代替equals()对键 进行比较的散列映射。专为解决特殊问题而设计的
LinkedHashMap
import java.util.*;
public class Test {
public static void main(String args[]) {
Map<Integer, String > data = new HashMap<Integer, String > ();
for (int i = 0; i < 10 ; i ++) {
data.put (i , "" + i + i + i );
}
Map <Integer , String > map1 = new LinkedHashMap<Integer, String > (data);
Map<Integer, String > map2 = new LinkedHashMap<Integer, String > (16, 0.75f, true);//分别为容量、负载因子和使用LRU算法
map2.putAll(data);
System.out.println(map1);
System.out.println(map2);
for (int i = 0; i < 5 ; i ++) {
map1.get (i );
map2.get (i );
}
System.out.println (map1 );
System.out.println (map2 );
map2.get (0 );
System.out.println (map2 );
}
}
----------------- 执行结果
{0 =000, 1 =111, 2 =222, 3 =333, 4 =444, 5 =555, 6 =666, 7 =777, 8 =888, 9 =999}
{0 =000, 1 =111, 2 =222, 3 =333, 4 =444, 5 =555, 6 =666, 7 =777, 8 =888, 9 =999}
{0 =000, 1 =111, 2 =222, 3 =333, 4 =444, 5 =555, 6 =666, 7 =777, 8 =888, 9 =999}
{5 =555, 6 =666, 7 =777, 8 =888, 9 =999, 0 =000, 1 =111, 2 =222, 3 =333, 4 =444}
{5 =555, 6 =666, 7 =777, 8 =888, 9 =999, 1 =111, 2 =222, 3 =333, 4 =444, 0 =000}
散列和散列码
当我们使用自定义的类型做为HashMap 的key 时,我们需要注意重写它的hashCode() 和equals() 方法。来看下面一个例子:
import java.util.*;
public class Test {
public static void main (String args[]) {
Map<Holder, Integer> m = new HashMap<Holder, Integer>();
for (int i=0 ; i<10 ; i++) {
m.put(new Holder(i), 9 - i);
}
System.out .println(m);
System.out .println(m.containsKey(new Holder(9 )));
}
}
class Holder {
private int i;
public Holder (int i) {
this .i = i;
}
public String toString () {
return String.valueOf(i);
}
}
默认情况下,hashCode() 计算的是地址值的散列值,所以上述代码执行结果为false 。所以我们要重写hashCode() 方法。不过还不够,源代码中还会对两个key 进行equals() 判断。这是因为两个值不同的key 也可能会得到相同的hash 值。所以以后如要要使用自定义的类型作为HashMap 或者HashMap 的子类(或者有关HashSet )的key ,则必须重写这两个方法。 一个正确的equals() 方法必须满足下列5个条件:
自反性。对任意不为null的x,x.equals(x)一定返回true。 对称性。对任意不为null的x,y。x.equals(y)==y.equals(x)。 传递性。对任意不为null的x,y,z。如果x.equals(y)为true,y.equals(z)为true,则x.equals(z)为true。 一致性。对任意不为null的x和y。只要对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应该保持一致。 对任意不为null的x,x.equals(null) == false。
为速度而散列
关于散列函数速度快的原因,我们已经在第十一章分析过了。现在我们模仿HashMap 自定义一个新的SimpleHashMap 。
import java.util.*;
public class Test {
public static void main (String args[]) {
SimpleHashMap<String, String> m = new SimpleHashMap<String, String>();
for (int i = 0 ; i < 5 ; i++) {
m.put("" + i, "" + i + i + i);
}
System.out .println(m);
System.out .println(m.get ("1" ));
System.out .println(m.put("2" , "2222" ));
System.out .println(m);
}
}
class SimpleHashMap<K, V> extends AbstractMap<K, V> {
private static final int SIZE = 997 ;
private LinkedList<MapEntry<K, V>>[] buckets;
@SuppressWarnings("unchecked" )
public SimpleHashMap () {
buckets = new LinkedList[SIZE];
}
public Set<Map.Entry<K, V>> entrySet () {
Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K, V>>();
for (LinkedList<MapEntry<K, V>> bucket : buckets) {
if (bucket == null ) continue ;
for (MapEntry<K, V> entry : bucket) {
set .add(entry);
}
}
return set ;
}
public V get (Object key) {
int index = Math.abs(key.hashCode()) % SIZE;
if (buckets[index] == null ) return null ;
for (MapEntry<K, V> entry : buckets[index]) {
if (entry.getKey().equals(key)) {
return entry.getValue();
}
}
return null ;
}
public V put (K key, V value ) {
V oldValue = null ;
int index = Math.abs(key.hashCode()) % SIZE;
if (buckets[index] == null ) {
buckets[index] = new LinkedList<MapEntry<K, V>>();
}
MapEntry<K, V> entry = new MapEntry<K, V>(key, value );
ListIterator<MapEntry<K, V>> it = buckets[index].listIterator();
boolean found = false ;
while (it.hasNext()) {
MapEntry<K, V> iPair = it.next();
if (iPair.getKey().equals(key)) {
oldValue = iPair.getValue();
it.set (entry);
found = true ;
break ;
}
}
if (!found) {
buckets[index].add(entry);
}
return oldValue;
}
static class MapEntry<K, V> implements Map.Entry<K, V> {
private K key;
private V value ;
public MapEntry (K key, V value ) {
this .key = key;
this .value = value ;
}
public K getKey () {
return key;
}
public V getValue () {
return value ;
}
public V setValue (V value ) {
V oldValue = this .value ;
this .value = value ;
return oldValue;
}
}
}
------------------执行结果
{2 =222 , 4 =444 , 0 =000 , 1 =111 , 3 =333 }
111
222
{2 =2222 , 4 =444 , 0 =000 , 1 =111 , 3 =333 }
注意到上述SIZE使用了一个质数。虽然此举能使散列分布的更加均匀,但其不一定能使查询更快。主要原因是除法和取余操作(a%b = a-(a / b)*b ) 是最慢的操作。如果使用2的整数次方作为散列表的长度,可用掩码代替除法(a % b = a & (b - 1 )) 。下面是分析为何可以简化取余操作。
当b是2 ^5 时。b可用表示成 100000
此时a 对b取余 其实就是直接取后五位。
因为(a / b)相当于就是右移5 位,*b就是再左移5 位。如此就是置a 右边五位数字为0 。
比如a 原先是11101101 ,那么(a / b)*b就是11100000
如此 a - (a / b)*b 就是01101 了。
也就相当于11101101 和 00011111 的与操作的结果,如此即可简化取余操作
覆盖hashCode()
hashCode() 用于对应下标,因此编写一个合理的hashCode() 显得尤为重要。hashCode() 产生的int值不必是唯一的,但是其产生速度必须快,且尽量是均匀的。当然其必须基于对象的内容 生成散列码,不保持一致性 会使这个方法毫无意义。如何生成尽量均匀的散列码呢?以下是一些基本指导:
给int变量result赋予某个非零值常量,例如17 为对象内每个有意义的域f(即可用做equals操作的域)计算一个int散列码c(见下表),对于基本类型,通常转成包装类型,直接调用c.hashCode()即可。 合并计算得到的散列码:result = 37 * result + c 返回result 检查hashCode()最后生成的结果,确保相同的对象有相同的散列码。
域类型 计算 boolean c = (f?0:1) byte、char、short或int c = (int)f long c = (int)(f ^ (f>>>32)) (高32位和第32位做异或操作) float c = Float.floatToIntBits(f) double long l = Double.doubleToLongBits(f);c = (int)(l ^ (l>>>32)) Object,其equals()调用了这个域的equals() c = f.hashCode() (如果这个类型也是自定义的,那么我们得先完成这个hashCode()方法) 数组 对每个元素应用上述规则
选择接口的不同实现
现在已经知道了,尽管实际上只有四种容器:Map、List、Set和Queue,但是每个接口都有不止一个实现版本,应该如何选择使用哪一个实现呢?
性能测试框架
书上搞了一套测试框架,我也简单跟着做了一下,来看下列框架中的主要三个类:
package test;
/**
* 单元测试类,需要重写test方法
*/
public abstract class Test <C > {
String name;
public Test(String name) {
this .name = name;
}
/**
* @param container 测试容器对象
* @param tp 测试参数
* @return 测试次数
*/
abstract int test(C container, TestParam tp);
}
package test;
/**
* 测试参数类
*/
public class TestParam {
public final int size;
public final int loops;
public TestParam (int size, int loops) {
this .size = size;
this .loops = loops;
}
public static TestParam[] array (int ... values) {
int size = values.length / 2 ;
TestParam[] result = new TestParam[size];
int n = 0 ;
for (int i = 0 ; i < size; i++) {
result[i] = new TestParam(values[n++], values[n++]);
}
return result;
}
public static TestParam[] array (String... values) {
int [] vals = new int [values.length];
for (int i = 0 ; i < vals.length; i++) {
vals[i] = Integer.parseInt(values[i]);
}
return array(vals);
}
}
package test;
import java.util.List;
public class Tester<C> {
public static TestParam[] defaultParams = TestParam.array(
10 , 5000 , 100 , 5000 , 1000 , 5000 , 10000 , 500 );
protected C initalize (int size) {
return container;
}
public static int fieldWidth = 8 ;
protected C container;
private String headline = "" ;
private List<Test<C>> tests;
private static String stringField () {
return "%" + fieldWidth + "s" ;
}
private static String numberField () {
return "%" + fieldWidth + "d" ;
}
private static int sizeWidth = 5 ;
private static String sizeField = "%" + sizeWidth + "s" ;
private TestParam[] paramList = defaultParams;
public Tester (C container, List<Test<C>> tests) {
this .container = container;
this .tests = tests;
if (container != null ) {
headline = container.getClass().getSimpleName();
}
}
public Tester (C container, List<Test<C>> tests, TestParam[] paramList) {
this (container, tests);
this .paramList = paramList;
}
public void setHeadline (String newHeadline) {
headline = newHeadline;
}
public static <C> void run (C cntnr, List<Test<C>> tests) {
new Tester<C>(cntnr, tests).timedTest();
}
public static <C> void run (C cntnr, List<Test<C>> tests, TestParam[] paramList) {
new Tester<C>(cntnr, tests, paramList).timedTest();
}
public void timedTest () {
displayHeader();
for (TestParam param : paramList) {
System.out .printf(sizeField, param.size);
for (Test<C> test : tests) {
C kontainer = initalize(param.size);
long start = System.nanoTime();
int reps = test.test(kontainer, param);
long duration = System.nanoTime() - start;
long timePerRep = duration / reps;
System.out .printf(numberField(), timePerRep);
}
System.out .println();
}
}
private void displayHeader () {
int width = fieldWidth * tests.size() + sizeWidth;
int dashLength = width - headline.length() - sizeWidth;
StringBuilder head = new StringBuilder(width);
for (int i = 0 ; i < dashLength / 2 ; i++) {
head.append('-' );
}
head.append(' ' );
head.append(headline);
head.append(' ' );
for (int i = 0 ; i < dashLength / 2 ; i++) {
head.append('-' );
}
System.out .println(head);
System.out .printf(sizeField, "size" );
for (Test test : tests) {
System.out .printf(stringField(), test.name);
}
System.out .println();
}
}
package test;
import java.util.*;
public class ListPerformance {
static Random rand = new Random();
static int reps = 1000 ;
static List <Test<List <Integer>>> tests =
new ArrayList<Test<List <Integer>>>();
static {
tests.add(new Test<List <Integer>>("add" ) {
int test(List <Integer> list , TestParam tp) {
int loops = tp.loops;
int listSize = tp.size;
for (int i = 0 ; i < loops; i++) {
list .clear();
for (int j = 0 ; j < listSize; j++) {
list .add(j);
}
}
return loops * listSize;
}
});
tests.add(new Test<List <Integer>>("get" ) {
int test(List <Integer> list , TestParam tp) {
int loops = tp.loops * reps;
int listSize = list .size();
for (int i = 0 ; i < loops; i++) {
list .get(rand.nextInt(listSize));
}
return loops;
}
});
tests.add(new Test<List <Integer>>("set" ) {
int test(List <Integer> list , TestParam tp) {
int loops = tp.loops * reps;
int listSize = list .size();
for (int i = 0 ; i < loops; i++) {
list .set(rand.nextInt(listSize), 47 );
}
return loops;
}
});
tests.add(new Test<List <Integer>>("iteradd" ) {
int test(List <Integer> list , TestParam tp) {
final int LOOPS = 1000000 ;
int half = list .size() / 2 ;
ListIterator<Integer> it = list .listIterator(half);
for (int i = 0 ; i < LOOPS; i++) {
it.add(47 );
}
return LOOPS;
}
});
tests.add(new Test<List <Integer>>("insert" ) {
int test(List <Integer> list , TestParam tp) {
int loops = tp.loops;
for (int i = 0 ; i < loops; i++) {
list .add(5 , 47 );
}
return loops;
}
});
tests.add(new Test<List <Integer>>("remove" ) {
int test(List <Integer> list , TestParam tp) {
int loops = tp.loops;
int size = tp.size;
for (int i = 0 ; i < loops; i++) {
list .clear();
list .addAll(Collections.nCopies(size, 47 ));
while (list .size() > 5 ) {
list .remove(5 );
}
}
return loops * size - 5 ;
}
});
}
static class ListTester extends Tester <List <Integer >> {
public ListTester(List <Integer> container, List <Test<List <Integer>>> tests) {
super(container, tests);
}
protected List <Integer> initalize(int size) {
container.clear();
container.addAll(Collections.nCopies(size, 47 ));
return container;
}
public static void run(List <Integer> container, List <Test<List <Integer>>> tests) {
new ListTester(container, tests).timedTest();
}
}
public static void main(String args[]) {
ListTester.run(new ArrayList<Integer>(), tests);
ListTester.run(new LinkedList<Integer>(), tests);
ListTester.run(new Vector<Integer>(), tests);
}
}
----------------------运行结果:
------------------- ArrayList -------------------
size add get set iteradd insert remove
10 63 10 11 19 431 69
100 11 10 10 17 449 24
1000 9 10 10 91 566 96
10000 7 10 11 807 1805 883
------------------- LinkedList -------------------
size add get set iteradd insert remove
10 63 21 23 13 143 64
100 7 32 31 8 70 18
1000 11 267 262 9 44 14
10000 13 3012 2920 9 65 10
--------------------- Vector ---------------------
size add get set iteradd insert remove
10 61 14 13 19 460 52
100 11 13 12 21 453 19
1000 8 13 12 95 601 103
10000 7 15 13 829 1881 883
测试结果横向比较是没有意义的。我们通过纵向比较,可以得出LinkedList 对于随机访问的能力很差,但是在add 和insert 或者remove 的时候就比较快。(不过要注意insert和remove都是写死在下标为5的位置。如果设在端点会因为LinkedList自身的优化对比较产生影响,设在任意下标又会因为随机访问而产生影响,所以此处设死在5可以规避这两个问题。 )另外注意到size 小的时候,平均时间偏大。这可能是测试次数较少的原因。从整体上与我们之前所了解的没有出路。关于其余的集合测试类,我就不写了。如果感兴趣模仿第四个类即可。
实用方法
import java.util .*
public class Test {
public static void main(String args[]) {
List<Integer> list = new ArrayList<Integer>()
//这个主要是用来区别方法的使用范围
Collection<Integer> collection = list
Collections.addAll (list, 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 )
System.out .println (Collections.max (collection))
System.out .println (Collections.min (collection))
//一个比较器 用于比较离5 的距离
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Math.abs (o1 - 5 ) - Math.abs (o2 - 5 )
}
}
System.out .println (Collections.max (collection, comparator))
System.out .println (Collections.min (collection, comparator))
//如果直接使用子列表会对源列表产生影响,此处重新new一个List
List<Integer> sublist_1_3 = new ArrayList<Integer>(list.subList (1 , 3 ))
System.out .println (sublist_1_3)
System.out .println (Collections.indexOfSubList (list, sublist_1_3))
sublist_1_3.remove (0 )
System.out .println (Collections.indexOfSubList (list, sublist_1_3))
sublist_1_3.add (250 )
System.out .println (Collections.indexOfSubList (list, sublist_1_3))
//lastIndexOfSubList 差不多此处省略
Collections.replaceAll (list, 3 , 2 )
System.out .println (list)
Collections.replaceAll (list, 2 , 520 )
System.out .println (list)
Collections.reverse (list)
System.out .println (list)
Collections.sort (list)
System.out .println (list)
Comparator<Integer> com1 = Collections.reverseOrder ()
Collections.sort (list, com1)
System.out .println (list)
Comparator<Integer> com2 = Collections.reverseOrder (comparator)
Collections.sort (list, com2)
System.out .println (list)
Collections.rotate (list, 5 )
System.out .println (list)
Collections.shuffle (list)
System.out .println (list)
Collections.shuffle (list, new Random(System.currentTimeMillis ()))
System.out .println (list)
System.out .println (sublist_1_3)
Collections.copy (list, sublist_1_3)
System.out .println (list)
//Collections.copy (list, Collections.nCopies (11 , 1 ))
Collections.swap (list, 0 , 9 )
System.out .println (list)
//Collections.fill (list, 1 )
//System.out .println (list)
//这个主要是用来区别方法的使用范围
Collection<Integer> collection2 = sublist_1_3
System.out .println (Collections.disjoint (collection, collection2))
Collections.replaceAll (sublist_1_3, 3 , 250 )
Collections.replaceAll (sublist_1_3, 250 , 333 )
System.out .println (collection)
System.out .println (collection2)
System.out .println (Collections.disjoint (collection, collection2))
System.out .println (Collections.frequency (collection, 520 ))
System.out .println (Collections.frequency (collection2, 333 ))
}
}
这里基本上所有想的到的都有,想不到的也有。所以遇到类似需求先找找jdk中有没有现成的方法。
持有引用
java.lang.ref 类库中包含了一组类,这些类为垃圾回收提供了更大的灵活性。当存在可能会耗尽内存的大对象时,这些类显得格外有用。如果想继续持有对某个对象的引用,希望以后还能够访问到该对象,但是也希望能够允许垃圾回收器释放它,这时就应该使用Reference 对象。 SoftReference 、WeakReference 和PhantomReference 由强到弱,对应不同级别的“可获得性”。使用SoftReference 和WeakReference 时,可以选择是否要将它们放入ReferenceQueue 。而PhantomReference 只能依赖与ReferenceQueue 。下面是一个示例:
package test2
import java.lang .ref .PhantomReference
import java.lang .ref .Reference
import java.lang .ref .ReferenceQueue
import java.lang .ref .SoftReference
import java.lang .ref .WeakReference
import java.util .*
class VeryBig {
private static final int SIZE = 10000
private long[] la = new long[SIZE]
private String ident
public VeryBig(String id) {
ident = id
}
public String toString() {
return ident
}
@Override
protected void finalize() {
System.out .println ("finalizing " + ident)
}
}
public class Test {
private static ReferenceQueue<VeryBig> rq =
new ReferenceQueue<VeryBig>()
public static void checkQueue() {
Reference<? extends VeryBig> inq = rq.poll ()
if (inq != null) {
System.out .println ("In queue: " + inq.get ())
}
}
public static void main(String args[]) throws Exception {
int size = 10
LinkedList<SoftReference<VeryBig>> sa =
new LinkedList<SoftReference<VeryBig>>()
for (int i = 0
sa.add (new SoftReference<VeryBig>(
new VeryBig("Soft " + i), rq))
System.out .println ("Just created: " + sa.getLast ())
checkQueue()
}
LinkedList<WeakReference<VeryBig>> wa =
new LinkedList<WeakReference<VeryBig>>()
for (int i = 0
wa.add (new WeakReference<VeryBig>(
new VeryBig("Weak " + i), rq))
System.out .println ("Just created: " + wa.getLast ())
checkQueue()
}
SoftReference<VeryBig> s =
new SoftReference<VeryBig>(new VeryBig("Soft" ))
WeakReference<VeryBig> w =
new WeakReference<VeryBig>(new VeryBig("Weak" ))
System.gc ()
LinkedList<PhantomReference<VeryBig>> pa =
new LinkedList<PhantomReference<VeryBig>>()
for (int i = 0
pa.add (new PhantomReference<VeryBig>(
new VeryBig("Phantom " + i), rq))
System.out .println ("Just created: " + pa.getLast ())
checkQueue()
}
}
}
--------执行结果:说实话我也不是很懂,看结果可以发现weak是会被回收的,Phantom也是会被回收的,但是没有加入rq队列,这个是我实验出来的。以后再去了解
Just created: java.lang .ref .SoftReference @15 db9742
Just created: java.lang .ref .SoftReference @6 d06d69c
Just created: java.lang .ref .SoftReference @7852 e922
Just created: java.lang .ref .SoftReference @4 e25154f
Just created: java.lang .ref .SoftReference @70 dea4e
Just created: java.lang .ref .SoftReference @5 c647e05
Just created: java.lang .ref .SoftReference @33909752
Just created: java.lang .ref .SoftReference @55 f96302
Just created: java.lang .ref .SoftReference @3 d4eac69
Just created: java.lang .ref .SoftReference @42 a57993
Just created: java.lang .ref .WeakReference @75 b84c92
Just created: java.lang .ref .WeakReference @6 bc7c054
Just created: java.lang .ref .WeakReference @232204 a1
Just created: java.lang .ref .WeakReference @4 aa298b7
Just created: java.lang .ref .WeakReference @7 d4991ad
Just created: java.lang .ref .WeakReference @28 d93b30
Just created: java.lang .ref .WeakReference @1 b6d3586
Just created: java.lang .ref .WeakReference @4554617 c
Just created: java.lang .ref .WeakReference @74 a14482
Just created: java.lang .ref .WeakReference @1540 e19d
finalizing Weak 6
Just created: java.lang .ref .PhantomReference @677327 b6
finalizing Weak 3
finalizing Weak 2
In queue: null
finalizing Weak 1
finalizing Weak 0
finalizing Weak
finalizing Weak 9
finalizing Weak 8
finalizing Weak 7
finalizing Weak 5
finalizing Weak 4
Just created: java.lang .ref .PhantomReference @14 ae5a5
In queue: null
Just created: java.lang .ref .PhantomReference @7 f31245a
In queue: null
Just created: java.lang .ref .PhantomReference @6 d6f6e28
In queue: null
Just created: java.lang .ref .PhantomReference @135 fbaa4
In queue: null
Just created: java.lang .ref .PhantomReference @45 ee12a7
In queue: null
Just created: java.lang .ref .PhantomReference @330 bedb4
In queue: null
Just created: java.lang .ref .PhantomReference @2503 dbd3
In queue: null
Just created: java.lang .ref .PhantomReference @4 b67cf4d
In queue: null
Just created: java.lang .ref .PhantomReference @7 ea987ac
In queue: null
而WeakHashMap 则是使用了WeakReference 包装key ,所以WeakHashMap 允许垃圾回收器自动清理键和值(Entry 继承于WeakReference )。
import java.util.WeakHashMap;
class Element {
private String ident;
private long [] ls = new long [10000 ];
public Element(String id) {
ident = id;
}
public String toString() {
return ident;
}
public int hashCode() {
return ident.hashCode();
}
public boolean equals(Object o) {
return o instanceof Element
&& ((Element) o).ident.equals(ident);
}
protected void finalize() {
System.out.println("Finalizing " +
getClass().getSimpleName() + " " + ident);
}
}
class Key extends Element {
public Key(String id) {
super (id);
}
}
class Value extends Element {
public Value(String id) {
super (id);
}
}
public class Test {
public static void main(String args[]) {
int size = 1000 ;
Key[] keys = new Key[size];
WeakHashMap<Key, Value> map =
new WeakHashMap<Key, Value>();
for (int i = 0 ; i < size; i++) {
Key k = new Key(Integer.toString(i));
Value v = new Value(Integer.toString(i));
if (i % 3 == 0 ) {
keys[i] = k;
}
map.put(k, v);
}
System.gc();
}
}
-----------存在数组中的没有被GC掉