《Java编程思想》学习笔记9——集合容器高级

1.Arrays.asList()方法产生的List是一个固定长度的数组,只支持不改变长度的操作,任何试图改变其底层数据结构长度的操作(如,增加,删除等操作)都会抛出UnsupportedOperationException异常。

为了使Arrays.asList()方法产生的List集合长度可变,可以将其作为集合容器的构造方法参数,如:

Set set = new HashSet(Arrays.asList(newint[]{1,23}));

或者将其作为Collections.addAll()方法的参数,如:

Collections.addAll(Arrays.asList(new int[]{1,23}));

2.SortedSet是一个对其元素进行排序了的Set,SortedSet接口有以下方法:

(1).Comparator comparator():

返回此Set中元素进行排序的比较器,如果该方法返回null,则默认使用自然排序。

(2).Object first():

返回Set中第一个(最低)元素。

(3).Object last():

返回Set中最后一个(最高)元素。

(4).SortedSet subset(fromElement, toElement):

返回此Set中从fromElement(包括)到toElement(不包括)的子Set。

(5).SortedSet headset(toElement):

返回此Set中元素严格小于toElement的子Set。

(6).SortedSet tailSet(fromElement):

返回此Set中元素大于等于fromElement的子Set。

3.SortedMap是一个根据Key排序的Map,SortedMap接口有以下方法:

(1).Comparator comparator():

返回此Map中key进行排序的比较器,如果返回的是null,则该Map的key使用自然排序。

(2).T firstKey():

返回此Map中第一个(最低)key。

(3).T lastKey();

返回此Map中最后一个(最高)key。

(4).SortedMap subMap(fromKey, toKey):

返回此Map中key从fromKey(包括)到toKey(不包括)的子Map。

(5).SortedMap headMap(toKey):

返回此Map中key严格小于toKey的子Map。

(6).SortedMap tailMap(fromKey):

返回此Map中key大于等于fromKey的Map。

4.HashMap/HashSet等Hash算法集合重写equals方法和hashCode方法:

HashSet,HashMap以及它们的子类等这些使用Hash算法的集合存放对象时,如果元素不是java中的8种基本类型(即元素都是对象类型),则必须重写对象的equals和hashCode方法,否则会产生一些错误,例如:

[java]  view plain copy
  1. Class A{  
  2.     int  i;  
  3.     public A(int i){  
  4.     this.i = i;  
  5. }  
  6. Public static void main(String args[]){  
  7.         Map map = new HashMap();  
  8.     map.put(new A(1), “First”);  
  9.     map.put(new A(2), “Second”);  
  10.     A a = new A(1);  
  11.     boolean b = map.containsKey(a);  
  12.     System.out.println(b);  
  13. }  
  14. }  

输出的结果是:false。

Map中有Key为A(1)对象,但是却没有找到,这是因为HashMap使用Hash算法根据对象的hashCode值来查找给定的对象,如果没有重写hashCode和equals方法,则对象默认使用Object的hashCode方法,Object默认hashCode方法使用对象的内存地址作为hashCode值。

为了使Hash算法集合存放对象类型数据符合用户的期望,必须重写对象的hashCode和equals方法,其中hashCode方法用于Hash算法的查找,equals方法用于对象比较,Hash算法中还要用到equals方法如下:

因为Hash算法所使用的hashCode可能会产生碰撞(不相等对象的hashCode值相同),当Hash算法产生碰撞时,就需要再次Hash(即再次通过其他方法计算hashCode值),所以一个对象使用Hash算法时,其对应的HashCode值可能不止一个,而是一组,当产生碰撞时就选择另一个hashCode值。当hashCode值产生碰撞时,还必须使用equals方法方法对象是否相等。

注意:由于Hash算法有可能会产生碰撞(不相等的对象hashCode值相同),所以hashCode和equals方法有如下关系:

(1).equals方法相等的对象,hashCode方法值一定相同。

(2).hashCode方法相同的对象,equals不一定相等。

5.创建只读集合容器:

List,Set和Map类型的集合容器都可以通过下面的方法创建为只读,即只可以访问,不能添加,删除和修改。

[java]  view plain copy
  1. static Collection<String> data = new ArrayList<String>();  
  2. data.add(“test”);  
  3. static Map<String, String> m = new HashMap<String, String>();  
  4. m.put(“key”, “value”);  

(1).只读集合:

[java]  view plain copy
  1. Collection<String> c = Collections.unmodifiableCollection(new ArrayList<String>(data));  
  2. System.out.println(c); //可以访问  
  3. //c.add(“test2”);只读,不可添加  

(2).只读List:

[java]  view plain copy
  1. List<String> list = Collections.unmodifiableList(new ArrayList<String>(data));  
  2. System.out.println(list.get(0)); //可以访问  
  3. //list.remove(0);只读,不可删除  

(3).只读Set:

[java]  view plain copy
  1. Set<String> set = Collections.unmodifiableSet(new HashSet<String>(data));  
  2. System.out.println(set.Iterator().next()) //可以访问  
  3. //set.add(“test”);只读,不可添加  

(4).只读Map:

[java]  view plain copy
  1. Map<String, String> map = Collections.unmodifiableMap(new HashMap<String, String>(m));  
  2. System.out.println(map.get(“key”)); //可以访问  
  3. //map.put(“key2”, “value2”);只读,不可添加  

只读集合容器会在编译时检查操作,如果对只读集合容器进行增删等操作时,将会抛出UnSupportedOperationException异常。

只读集合容器类似于将集合对象访问控制修饰符设置为private,不同之处在于,其他类可以访问,只是不能修改。

6.线程同步集合容器:

Java集合容器中,Vector,HashTable等比较古老的集合容器是线程安全的,即处理了多线程同步问题。

而Java2之后对Vector和HashTable的替代类ArrayList,HashSet,HashMap等一些常用的集合容器都是非线程安全的,即没有进行多线程同步处理。

Java中可以通过以下方法方便地将非线程安全的集合容器进行多线程同步:

(1).线程同步集合:

Collection<String> c= Collections.synchronizedCollection(newArrayList<String>());

(2).线程同步List:

List<String> c= Collections.synchronizedList(newArrayList<String>());

(3).线程同步Set:

Set<String> c= Collections.synchronizedSet(newHashSet<String>());

(4).线程同步Map:

Map<String> c= Collections.synchronizedMap(newHashMap<String, String>());

7.对象的强引用、软引用、弱引用和虚引用:

JDK1.2以前版本中,只存在一种引用——正常引用,即对象强引用,如果一个对象被一个引用变量所指向,则该对象是可触及(reached)的状态,JVM垃圾回收器不会回收它,弱一个对象不被任何引用变量指向,则该对象就处于不可触及的状态,垃圾回收器就会回收它。

从JDK1.2版本之后,为了更灵活的控制对象生命周期,引入了四种引用:强引用(java.lang.ref.Reference)、软引用(java.lang.ref.SoftReference)、弱引用(java.lang.ref.WeakReference)和虚引用(java.lang.ref.PhantomReference):

(1). 强引用(java.lang.ref.Reference):

    即Java程序中普遍使用的正常对象引用,存放在内存中得对象引用栈中,如果一个对象被强引用,则说明程序还在使用它,垃圾回收器不会回收,当内存不足时,JVM抛出内存溢出异常使程序异常终止。

(2). 软引用(java.lang.ref.SoftReference):

如果一个对象只具有软引用,则内存空间足够,垃圾回收器也不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,JVM就会把这个软引用加入到与之关联的引用队列中。

(3). 弱引用(java.lang.ref.WeakReference):

弱引用用来实现内存中对象的标准映射,即为了节约内存,对象的实例可以在一个程序内多处使用。

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的后台线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

(4). 虚引用(java.lang.ref.PhantomReference):

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

ReferenceQueue queue = new ReferenceQueue ();

PhantomReference pr = new PhantomReference (object, queue);

   JVM中,对象的引用往往都是很复杂的,各个对象之间相互引用形成一个内存引用树,java中某个对象是否可触及,有以下两条判断原则:

a.单条引用路径可及性判断:在这条路径中,最弱的一个引用决定对象的可及性。

b.多条引用路径可及性判断:几条路径中,最强的一条的引用决定对象的可及性。

软引用,弱引用,虚引用例子如下:

[java]  view plain copy
  1. package com.test.reference;  
  2. //测试对象  
  3. class VeryBig {  
  4.     private static final int SIZE = 10000;  
  5.     private long[] la = new long[SIZE];  
  6.     private String ident;  
  7.   
  8.     public VeryBig(String id) {  
  9.         ident = id;  
  10.     }  
  11.   
  12.     public String toString() {  
  13.         return ident;  
  14.     }  
  15.   
  16.     protected void finalize() {  
  17.         System.out.println("Finalizing " + ident);  
  18.     }  
  19. }  
  20.   
  21. package com.test.reference;  
  22.   
  23. import java.lang.ref.PhantomReference;  
  24. import java.lang.ref.Reference;  
  25. import java.lang.ref.ReferenceQueue;  
  26. import java.lang.ref.SoftReference;  
  27. import java.lang.ref.WeakReference;  
  28. import java.util.LinkedList;  
  29.   
  30. public class TestReferences {  
  31.     //引用队列  
  32.     private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();  
  33.     //检查引用队列是否为空  
  34.     public static void checkQueue() {  
  35.         //强引用,轮询引用队列,看是否有可以的对象引用  
  36.         Reference<? extends VeryBig> inq = rq.poll();  
  37.         if (inq != null)  
  38.             //如果有可以使用的对象引用,则打印出该引用指向的对象  
  39.             System.out.println("In queue: " + inq.get());  
  40.     }  
  41.   
  42.     public static void main(String[] args) {  
  43.         int size = 2;  
  44.         //创建存放VeryBig对象的软引用集合  
  45.         LinkedList<SoftReference<VeryBig>> sa = new LinkedList<SoftReference<VeryBig>>();  
  46.         for (int i = 0; i < size; i++) {  
  47.             //将对象和软引用添加到引用队列中  
  48.             sa.add(new SoftReference<VeryBig>(new VeryBig("Soft " + i), rq));  
  49.             System.out.println("Just created: " + sa.getLast());  
  50.             checkQueue();  
  51.         }  
  52. //创建存放VeryBig对象的弱引用集合  
  53.         LinkedList<WeakReference<VeryBig>> wa = new LinkedList<WeakReference<VeryBig>>();  
  54.         for (int i = 0; i < size; i++) {  
  55.             //将对象和弱引用添加到引用队列中  
  56.             wa.add(new WeakReference<VeryBig>(new VeryBig("Weak " + i), rq));  
  57.             System.out.println("Just created: " + wa.getLast());  
  58.             checkQueue();  
  59.         }  
  60.         SoftReference<VeryBig> s = new SoftReference<VeryBig>(new VeryBig(  
  61.                 "Soft"));  
  62.         WeakReference<VeryBig> w = new WeakReference<VeryBig>(new VeryBig(  
  63.                 "Weak"));  
  64.         //垃圾回收器回收,在回收之前调用对象的finalize()方法  
  65.         System.gc();  
  66. //创建存放VeryBig对象的虚引用集合  
  67.         LinkedList<PhantomReference<VeryBig>> pa = new LinkedList<PhantomReference<VeryBig>>();  
  68.         for (int i = 0; i < size; i++) {  
  69.             //将对象和虚引用添加到引用队列中  
  70.             pa.add(new PhantomReference<VeryBig>(new VeryBig("Phantom " + i),  
  71.                     rq));  
  72.             System.out.println("Just created: " + pa.getLast());  
  73.             checkQueue();  
  74.         }  
  75.     }  
  76. }  

输出结果为:

Just created:java.lang.ref.SoftReference@757aef

Just created:java.lang.ref.SoftReference@d9f9c3

Just created:java.lang.ref.WeakReference@9cab16

Just created:java.lang.ref.WeakReference@1a46e30

In queue: null

Finalizing Weak 0

Finalizing Weak

Finalizing Weak 1

Just created:java.lang.ref.PhantomReference@3e25a5

In queue: null

Just created:java.lang.ref.PhantomReference@19821f

注意:由于System.gc()只是通知JVM虚拟机可以进行垃圾回收器可以进行垃圾回收了,但是垃圾回收器具体什么时候允许说不清楚,所以这个输出结果只是个参考,每次运行的结果Finalize方法的执行顺序不太一样。

从程序可以看出,尽管对象被引用,垃圾回收器还是回收了被引用对象,引用队列总是创建一个包含null对象的引用。

8.WeakHashMap:

WeakHashMap专门用于存放弱引用,WeakHashMap很容易实现弱引用对象标准映射功能。

在WeakHashMap中,只存储一份对象的实例及其值,当程序需要对象实例值时,WeakHashMap从现有的映射中找出已存在的对象值映射。

由于弱引用节约内存的技术,WeakHashMap允许垃圾回收器自动清除器存放的key和value。WeakHashMap自动将其中存放的key和value包装为弱引用,当key不再被使用时,垃圾回收器自动回收该key和value。

你可能感兴趣的:(java)