java编程思想--17容器的深入研究

17.1完整的容器分类法
X
17.2填充容器
17.3Collection的功能和方法

其中并不包括随机访问的get方法,因为Collection是包含Set的,Set自己维护内部数据。
17.5List的功能方法
17.6Set和存储顺序

LinkedHashSet按照插入顺序进行排序,TreeSet按照排序顺序维护元素。
SortedSet
17.7Map和存储顺序

treeMap是现阶段SortedMap的唯一实现,可以确保键处于排序状态
LinkedHashMap以元素的插入顺序返回键值对,在构造器中设定采用LRU算法。
17.9散列与散列码
散列使得查找可以快速进行。瓶颈在键的查询速度,解决方案之一是保持键的排序状态。
查询一个值的过程首先是计算散列码,然后使用散列码查询数组。数组并不是保存值而是保存值的List
然后对List中的值使用equals()方法进行线性查询。如果散列函数做的好话,数组的每个值只有较少的值。因此不是查询整个List而 是跳到数组的某个位置,只对很少的元素比较。
设计HashCode最重要的因素:同一个对象调用HashCode()应该产生同样的值。
不应该使HashCode()依赖于具有唯一性的对象信息,比如对象的地址。
java.util.Collection中提供了丰富的静态方法。


集合中添加另一个集合的方法:
(1).Collection.addAll(被添加的Collection对象)方法:
如:list1.addAll(list2);
(2).Collections.addAll(添加到的目标Collection对象,可变参数的集合或者对象)方法:
如:Collections.addAll(list1, new Object1(), new Object2()…);
       Collectionns.addAll(list1, list2…);
注意:Collections是java集合容器的工具类,相比于(1),使用Collections的(2)更灵活。
2.Java集合中常用的交集、并集和差集操作:
并集:collection对象1.addAll(collection对象2);
交集:collection对象1. retainAll(collection对象2);
差集:collection对象1. removeAll(collection对象2);
注意:上述的集合操作时,集合元素的equals方法会影响操作结果。
3.将其他类型集合转换为List:
Arrays.asList(非List类型的集合对象/可变参数的对象);方法可以将传递的参数转变为List集合。如:Arrays.asList(new Object1(),new Object2(),…);
Arrays和Collections类似,是Array数组类型集合的工具类。
注意:Arrays.asList()方法转换后的List对象是一个size不能改变的对象,如果对该对象做增加或者删除元素操作时,将会报不支持的操作异常。
4.List集合:
List集合主要有两种具体的集合容器:ArrayList和LinkedList。
(1).ArrayList:底层实现是数组,提供了根据数组下标快速随机访问的能力,但是增加和删除元素时因为需要引动数组的元素,因此比较慢。
(2).LinkedList:底层实现是链表,链表访问元素时必须从链表头至链表尾挨个查找,因此只能顺序访问,速度比随机访问要慢。但是增加和删除元素时,只需要修改链表的指针而不需要移动元素,因此速度比较快。
5.LinkedList:
LinkedList除了实现了基本的List接口以外,还提供了一些特定的方法,使得LinkedList可以方便地实现Stack、Queue以及双端Queue的功能。
LinkedList提供的非List接口方法:
(1).getFirst():获取并且不移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。
(2).element():获取并且不移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。
(3).peek():获取并且不移除LinkedList集合中第一个元素。如果集合为空,则返回null。
(4).removeFirst():获取并且移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。
(5).remove():获取并且移除LinkedList集合中第一个元素。如果集合为空,抛出NoSuchElementException异常。
(6).poll():获取并且移除LinkedList集合中第一个元素。如果集合为空,则返回null。
(7).addFirst():向LinkedList集合的头部插入一个元素。
(8).add():向LinkedList集合的尾部插入一个元素。
(9).offer():向LinkedList集合的尾部插入一个元素。
(10).removeLast():获取并且移除LinkedList集合中最后一个元素。如果集合为空,抛出NoSuchElementException异常。
6.Iterator:
Iterator迭代器在java集合容器中应用比较广泛,对于List类型的集合,可以通过下标索引值获取到指定的元素,而对于Set类型的集合,因为Set是没有索引的,因此只能通过迭代器来遍历。
Iterator迭代器是一个顺序选择和遍历集合元素的对象,使用者不需要关心其底层的数据结构和实现方式。Java中的Iterator迭代器是单向的。
Iterator的常用方法如下:
(1).collection对象.iterator()方法:将集合对象转换为Iterator迭代器。
(2).iterator对象.hasNext()方法:判断迭代器中是否还有元素。
(3).iterator对象.next()方法:获取迭代器中下一个元素。
(4).iterator对象.remove()方法:删除迭代器中当前元素。
注意:使用迭代器的好处是,当数据结构从List变为Set之后,迭代集合的相关代码一点都不用改变。
7.ListIterator:
ListIterator是Iterator的子类,它只能有List类型的集合产生,ListIterator是一个双向的迭代器,即它可以向前和向后双向遍历集合。ListIterator的常用方法如下:
(1).list类型对象.listIterator():将List类型的集合转换为ListIterator迭代器。
(2).list类型对象.listIterator(int n):将List类型的集合转换为ListIterator迭代器,同时指定迭代器的起始元素为第n个元素。
(3).listIterator对象.hasNext():判断迭代器中是否还有下一个元素。
(4).listIterator对象.next():获取迭代器中的下一个元素。
(5).listIterator对象.hasPrevious():判断迭代器中是否还有前一个元素。
(6).listIterator对象.previous():获取迭代器中的前一个元素。
(7).listIterator对象.set(元素对象):将当前迭代到的元素设置为另一个值。
8.Map遍历3中方法:
Mapmap = new HashMap();
map.put(“test1”, object1);
……
map.put(“testn” , objectn);
(1).Map的values()方法可以获取Map值的集合:
Iterator it = map.values().iterator();
while(it.hasNext()){
Object obj = it.next();
}

(2).Map的keySet方法可以获取Map键的Set集合:
Set keys = map.keySet();
for(Iterator it = key.iterator(); it.hasNext(); ){
String key = it.next();
Object obj = map.get(key);
}

(3).通过使用Entry来得到Map的key和value:
Set> entrySet = map.entrySet();
for(Iterator > it = entrySet.iterator(); it.hasNext(); ){
Map.Entry entry = it.next();
String key = entry.getKey();
Object value = entry.getValue();
}

9.Collection和Iterator:
Collection是java中除了Map以外的集合容器的通用接口,如果想自定义一种集合容器类型的类,可以选择实现Collection接口或者继承Collection的子类。
实现Collection接口或者继承Collection子类的时候,必须实现Collection接口的所有方法,而Iterator为定义自定义集合容器类型提供了另一种方便,Iterator是一种轻量级的接口,只需要实现hasNext(),next()方法即可,remove()方法是可选方法。
注意:Foreach循环支持所有实现了Iterable接口的集合容器(Collection接口的父接口是Iterable),Map集合没有实现Iterable接口,因此不支持Foreach循环。
10.java集合容器框架图:


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 plaincopy
Class A{  
    int  i;  
    public A(int i){  
    this.i = i;  
}  
Public static void main(String args[]){  
        Map map = new HashMap();  
    map.put(new A(1), “First”);  
    map.put(new A(2), “Second”);  
    A a = new A(1);  
    boolean b = map.containsKey(a);  
    System.out.println(b);  
}  
}  

输出的结果是: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 plaincopy
static Collection data = new ArrayList();  
data.add(“test”);  
static Map m = new HashMap();  
m.put(“key”, “value”);  

(1).只读集合:
[java] view plaincopy
Collection c = Collections.unmodifiableCollection(new ArrayList(data));  
System.out.println(c); //可以访问  
//c.add(“test2”);只读,不可添加  

(2).只读List:
[java] view plaincopy
List list = Collections.unmodifiableList(new ArrayList(data));  
System.out.println(list.get(0)); //可以访问  
//list.remove(0);只读,不可删除  

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

(4).只读Map:
[java] view plaincopy
Map map = Collections.unmodifiableMap(new HashMap(m));  
System.out.println(map.get(“key”)); //可以访问  
//map.put(“key2”, “value2”);只读,不可添加  

只读集合容器会在编译时检查操作,如果对只读集合容器进行增删等操作时,将会抛出UnSupportedOperationException异常。
只读集合容器类似于将集合对象访问控制修饰符设置为private,不同之处在于,其他类可以访问,只是不能修改。
6.线程同步集合容器:
Java集合容器中,Vector,HashTable等比较古老的集合容器是线程安全的,即处理了多线程同步问题。
而Java2之后对Vector和HashTable的替代类ArrayList,HashSet,HashMap等一些常用的集合容器都是非线程安全的,即没有进行多线程同步处理。
Java中可以通过以下方法方便地将非线程安全的集合容器进行多线程同步:
(1).线程同步集合:
Collection c= Collections.synchronizedCollection(newArrayList());
(2).线程同步List:
List c= Collections.synchronizedList(newArrayList());
(3).线程同步Set:
Set c= Collections.synchronizedSet(newHashSet());
(4).线程同步Map:
Map c= Collections.synchronizedMap(newHashMap());
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 plaincopy
package com.test.reference;  
//测试对象  
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;  
    }  
  
    protected void finalize() {  
        System.out.println("Finalizing " + ident);  
    }  
}  
  
package com.test.reference;  
  
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.LinkedList;  
  
public class TestReferences {  
    //引用队列  
    private static ReferenceQueue rq = new ReferenceQueue();  
    //检查引用队列是否为空  
    public static void checkQueue() {  
        //强引用,轮询引用队列,看是否有可以的对象引用  
        Reference inq = rq.poll();  
        if (inq != null)  
            //如果有可以使用的对象引用,则打印出该引用指向的对象  
            System.out.println("In queue: " + inq.get());  
    }  
  
    public static void main(String[] args) {  
        int size = 2;  
        //创建存放VeryBig对象的软引用集合  
        LinkedList> sa = new LinkedList>();  
        for (int i = 0; i < size; i++) {  
            //将对象和软引用添加到引用队列中  
            sa.add(new SoftReference(new VeryBig("Soft " + i), rq));  
            System.out.println("Just created: " + sa.getLast());  
            checkQueue();  
        }  
//创建存放VeryBig对象的弱引用集合  
        LinkedList> wa = new LinkedList>();  
        for (int i = 0; i < size; i++) {  
            //将对象和弱引用添加到引用队列中  
            wa.add(new WeakReference(new VeryBig("Weak " + i), rq));  
            System.out.println("Just created: " + wa.getLast());  
            checkQueue();  
        }  
        SoftReference s = new SoftReference(new VeryBig(  
                "Soft"));  
        WeakReference w = new WeakReference(new VeryBig(  
                "Weak"));  
        //垃圾回收器回收,在回收之前调用对象的finalize()方法  
        System.gc();  
//创建存放VeryBig对象的虚引用集合  
        LinkedList> pa = new LinkedList>();  
        for (int i = 0; i < size; i++) {  
            //将对象和虚引用添加到引用队列中  
            pa.add(new PhantomReference(new VeryBig("Phantom " + i),  
                    rq));  
            System.out.println("Just created: " + pa.getLast());  
            checkQueue();  
        }  
    }  
}  

输出结果为:
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。

1,jdk5增加如下接口:
 
Queue接口和实现PriorityQueue和各种风格的BlockingQueue.
ConcurrentMap接口和实现ConcurrentHashMap.它们用于多线程机制。
CoppyOnWriteArrayList和CoppyOnWriteArraySet,它们也是用于多线程机制。
EnumSet和EnumMap,为了使用Enum而设计的Set和Map的特殊实现。
Collections类的多个便利方法。
2,Collections类的ncopies()和fill()方法。
 
class StringAddress {
  private String s;
  public StringAddress(String s) { this.s = s; }
  public String toString() {
    return super.toString() + " " + s;
  }
}
public class FillingLists {
  public static void main(String[] args) {
    List list= new ArrayList(
      Collections.nCopies(4, new StringAddress("Hello")));
    System.out.println(list);
    Collections.fill(list, new StringAddress("World!"));
    System.out.println(list);
  }
} /* Output: (Sample
 
3,使用Abstract类
 
    可以使用定制的Collection和Map实现,每个java.util容器都有自己的Abstract类,它们提供了该容器的部分实现,
 
因此你必须做的只是去实现那些产生想要的容器所必须的方法。
 
4,Collection的功能方法
 
boolean add(T)
boolean addAll(Collection)
void clear()
boolean contains(T)
Boolean containsAll(Collection(?))
boolean isEmpty()
Iterator iterator()
Boolean remove(Object)
boolean removeAll(Collection)
Boolean retain(Collection)
int size();
Object[] toArray()
T[] toArray(T[] a)
 
 
public class CollectionMethods {
  public static void main(String[] args) {
    Collection c = new ArrayList();
    c.addAll(Countries.names(6));
    c.add("ten");
    c.add("eleven");
    print(c);
    // Make an array from the List:
    Object[] array = c.toArray();
    // Make a String array from the List:
    String[] str = c.toArray(new String[0]);
    // Find max and min elements; this means
    // different things depending on the way
    // the Comparable interface is implemented:
    print("Collections.max(c) = " + Collections.max(c));
    print("Collections.min(c) = " + Collections.min(c));
    // Add a Collection to another Collection
    Collection c2 = new ArrayList();
    c2.addAll(Countries.names(6));
    c.addAll(c2);
    print(c);
    c.remove(Countries.DATA[0][0]);
    print(c);
    c.remove(Countries.DATA[1][0]);
    print(c);
    // Remove all components that are
    // in the argument collection:
    c.removeAll(c2);
    print(c);
    c.addAll(c2);
    print(c);
    // Is an element in this Collection?
    String val = Countries.DATA[3][0];
    print("c.contains(" + val  + ") = " + c.contains(val));
    // Is a Collection in this Collection?
    print("c.containsAll(c2) = " + c.containsAll(c2));
    Collection c3 =
      ((List)c).subList(3, 5);
    // Keep all the elements that are in both
    // c2 and c3 (an intersection of sets):
    c2.retainAll(c3);
    print(c2);
    // Throw away all the elements
    // in c2 that also appear in c3:
    c2.removeAll(c3);
    print("c2.isEmpty() = " +  c2.isEmpty());
    c = new ArrayList();
    c.addAll(Countries.names(6));
    print(c);
    c.clear(); // Remove all elements
    print("after c.clear():" + c);
  }
} /* Output:
 
5,UnsurpportedOperationException
 
来源于固定尺寸的数据结构支持的容器。当你用Array.asList()将数组转换为list时,会得到这样的容器。你还可以使用
 
Collections类中的“不可修改”的方法,选择创建任何会抛出UnsurpportedOperationException的容器
 

public class Unsupported {
  static void test(String msg, List list) {
    System.out.println("--- " + msg + " ---");
    Collection c = list;
    Collection subList = list.subList(1,8);
    // Copy of the sublist:
    Collection c2 = new ArrayList(subList);
    try { c.retainAll(c2); } catch(Exception e) {
      System.out.println("retainAll(): " + e);
    }
    try { c.removeAll(c2); } catch(Exception e) {
      System.out.println("removeAll(): " + e);
    }
    try { c.clear(); } catch(Exception e) {
      System.out.println("clear(): " + e);
    }
    try { c.add("X"); } catch(Exception e) {
      System.out.println("add(): " + e);
    }
    try { c.addAll(c2); } catch(Exception e) {
      System.out.println("addAll(): " + e);
    }
    try { c.remove("C"); } catch(Exception e) {
      System.out.println("remove(): " + e);
    }
    // The List.set() method modifies the value but
    // doesn't change the size of the data structure:
    try {
      list.set(0, "X");
    } catch(Exception e) {
      System.out.println("List.set(): " + e);
    }
  }
  public static void main(String[] args) {
    List list =
      Arrays.asList("A B C D E F G H I J K L".split(" "));
    test("Modifiable Copy", new ArrayList(list));
    test("Arrays.asList()", list);
    test("unmodifiableList()",
      Collections.unmodifiableList(
        new ArrayList(list)));
  }
} /* Output:
6,ConcurrentModificationException.
 
public class FailFast {
  public static void main(String[] args) {
    Collection c = new ArrayList();
    Iterator it = c.iterator();
    c.add("An object");
    try {
      String s = it.next();
    } catch(ConcurrentModificationException e) {
      System.out.println(e);
    }
  }
} /*
 
7,持有引用
 
    java.lang.ref类库包含了一组类,这些类为垃圾回收提供而来更大的灵活性。当存在可能会损耗内存的大对象的时候。
 
这些类显得很有用。有三个继承自抽象类Reference的类:SoftReference,WeakReference,PhantomReference.
 
 
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; }
  protected void finalize() {
    System.out.println("Finalizing " + ident);
  }
}
public class References {
  private static ReferenceQueue rq =
    new ReferenceQueue();
  public static void checkQueue() {
    Reference inq = rq.poll();
    if(inq != null)
      System.out.println("In queue: " + inq.get());
  }
  public static void main(String[] args) {
    int size = 10;
    // Or, choose size via the command line:
    if(args.length > 0)
      size = new Integer(args[0]);
    LinkedList> sa =
      new LinkedList>();
    for(int i = 0; i < size; i++) {
      sa.add(new SoftReference(
        new VeryBig("Soft " + i), rq));
      System.out.println("Just created: " + sa.getLast());
      checkQueue();
    }
    LinkedList> wa =
      new LinkedList>();
    for(int i = 0; i < size; i++) {
      wa.add(new WeakReference(
        new VeryBig("Weak " + i), rq));
      System.out.println("Just created: " + wa.getLast());
      checkQueue();
    }
    SoftReference s =
      new SoftReference(new VeryBig("Soft"));
    WeakReference w =
      new WeakReference(new VeryBig("Weak"));
    System.gc();
    LinkedList> pa =
      new LinkedList>();
    for(int i = 0; i < size; i++) {
      pa.add(new PhantomReference(
        new VeryBig("Phantom " + i), rq));
      System.out.println("Just created: " + pa.getLast());
      checkQueue();
    }
  }
} /* (Execute to see output) *///:~
 
WeakHashMap用来保存WeakReferece。
 
class Element {
  private String ident;
  public Element(String id) { ident = id; }
  public String toString() { return ident; }
  public int hashCode() { return ident.hashCode(); }
  public boolean equals(Object r) {
    return r instanceof Element &&
      ident.equals(((Element)r).ident);
  }
  protected void finalize() {
    System.out.println("Finalizing " +
      getClass().getSimpleName() + " " + ident);
  }
}
class Key extends Element {
  public Key(String id) { super(id); }
}
class Values extends Element {
  public Values(String id) { super(id); }
}
public class CanonicalMapping {
  public static void main(String[] args) {
    int size = 1000;
    // Or, choose size via the command line:
    if(args.length > 0)
      size = new Integer(args[0]);
    Key[] keys = new Key[size];
    WeakHashMap map =
      new WeakHashMap();
    for(int i = 0; i < size; i++) {
      Key k = new Key(Integer.toString(i));
      Values v = new Values(Integer.toString(i));
      if(i % 3 == 0)
        keys[i] = k; // Save as "real" references
      map.put(k, v);
    }
    System.gc();
  }
} /* (Execute to see output) *///:~
 
BitSet使用于保存大量“开关”信息。它的效率是对空间而言。如果需要高效的访问时间,BitSet比本地数组稍慢一点:
 
public class Bits {
  public static void printBitSet(BitSet b) {
    print("bits: " + b);
    StringBuilder bbits = new StringBuilder();
    for(int j = 0; j < b.size() ; j++)
      bbits.append(b.get(j) ? "1" : "0");
    print("bit pattern: " + bbits);
  }
  public static void main(String[] args) {
    Random rand = new Random(47);
    // Take the LSB of nextInt():
    byte bt = (byte)rand.nextInt();
    BitSet bb = new BitSet();
    for(int i = 7; i >= 0; i--)
      if(((1 << i) &  bt) != 0)
        bb.set(i);
      else
        bb.clear(i);
    print("byte value: " + bt);
    printBitSet(bb);
    short st = (short)rand.nextInt();
    BitSet bs = new BitSet();
    for(int i = 15; i >= 0; i--)
      if(((1 << i) &  st) != 0)
        bs.set(i);
      else
        bs.clear(i);
    print("short value: " + st);
    printBitSet(bs);
    int it = rand.nextInt();
    BitSet bi = new BitSet();
    for(int i = 31; i >= 0; i--)
      if(((1 << i) &  it) != 0)
        bi.set(i);
      else
        bi.clear(i);
    print("int value: " + it);
    printBitSet(bi);
    // Test bitsets >= 64 bits:
    BitSet b127 = new BitSet();
    b127.set(127);
    print("set bit 127: " + b127);
    BitSet b255 = new BitSet(65);
    b255.set(255);
    print("set bit 255: " + b255);
    BitSet b1023 = new BitSet(512);
    b1023.set(1023);
    b1023.set(1024);
    print("set bit 1023: " + b1023);
  }
} /* Output:

一、基本概念

Java容器类类库的用途是“持有对象”,并将其划分为两个不同的概念:
1)Collection:一个独立元素的序列,这些元素都服从一条或者多条规则。 List必须按照插入的顺序保存元素,而set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
2)Map:一组成对的“键值对”对象,允许你使用键来查找值。
|Collection
|  ├List
|  │-├LinkedList
|  │-├ArrayList
|  │-└Vector
|  │ └Stack
|  ├Set
|  │├HashSet
|  │├TreeSet
|  │└LinkedSet
|
|Map
  ├Hashtable
  ├HashMap
  └WeakHashMap
注: 1、java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
  2、java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
二、Collection集合接口

  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。  
主要方法: boolean add(Object o)添加对象到集合 boolean remove(Object o)删除指定的对象 int size()返回当前集合中元素的数量 boolean contains(Object o)查找集合中是否有指定的对象 boolean isEmpty()判断集合是否为空 Iterator iterator()返回一个迭代器 boolean containsAll(Collection c)查找集合中是否有集合c中的元素 boolean addAll(Collection c)将集合c中所有的元素添加给该集合 void clear()删除集合中所有元素 void removeAll(Collection c)从集合中删除c集合中也有的元素 void retainAll(Collection c)从集合中删除集合c中不包含的元素
1、List接口

  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
   实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
  1)LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(…));
  2) ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并 没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。一般情况下使用这两个就可以了,因为非同步,所以效率比较高。
如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  3)Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个 Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例 如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该 异常。
  4)Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方 法,还有 peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
2、Set接口

  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。 Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  Set容器类主要有HashSet和TreeSet等。
  
  1)HashSet类
   Java.util.HashSet类实现了Java.util.Set接口。
  -> 它不允许出现重复元素;
  -> 不保证和政集合中元素的顺序
  ->允许包含值为null的元素,但最多只能有一个null元素。
public class TestHashSet { public static void main(String [] args) { HashSet h=new HashSet(); h.add(1st); h.add(2nd); h.add(new Integer(3)); h.add(new Double(4.0)); h.add(2nd); //重复元素,未被添加 h.add(new Integer(3)); //重复元素,未被添加 h.add(new Date()); System.out.println(开始:size=+h.size()); Iterator it=h.iterator(); while(it.hasNext()) { Object o=it.next(); System.out.println(o); } h.remove(2nd); System.out.println(移除元素后:size=+h.size()); System.out.println(h); } }
  2)TreeSet
  TreeSet描述的是Set的一种变体——可以实现排序等功能的集合,它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成的读uixiangxulie时刻按照“升序”排列。
public class TestTreeSet { public static void main(String [] args) { TreeSet ts=new TreeSet(); ts.add(orange); ts.add(apple); ts.add(banana); ts.add(grape); Iterator it=ts.iterator(); while(it.hasNext()) { String fruit=(String)it.next(); System.out.println(fruit); } } }
二、Map集合接口

  Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
主要方法: boolean equals(Object o)比较对象 boolean remove(Object o)删除一个对象 put(Object key,Object value)添加key和value Hashtable类
1、Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。**添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。**Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
2、HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。  
-JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。 HashTable的所有方法都是同步的。
-JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为 有条件的线程安全类。
-JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap, 它提供了更高的灵活性。同时进行的读和写操作都可以并发地
HashTable和HashMap区别
第一、继承不同。   public class Hashtable extends Dictionary implements Map   public class HashMap extends AbstractMap implements Map 第二、Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。 第三、Hashtable中,key和value都不允许出现null值。在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。 第四、两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。 第五、哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。 第六、Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
3、WeakHashMap类
  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

你可能感兴趣的:(java编程思想)