天天看点

java编程思想笔记(十) 集合容器深入

1.散列与散列码(hashcode)

hashcode基本就是把对象转换成了一串数字,常用的功能就是在集合容器里把这一串数字作为数组的下标,以实现快速插入到数组的功能,这样性能就能有效的提升。

当使用HashSet,HashMap这些集合容器存对象时,如果该对象不是java的8种基本类型中的,那就要重写hashcode和equals方法,不然后面会出现找不到该对象等等问题。

class Person {
	private String name;
	private int age;

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

public class Test1{
	public static void main(String[] args) {
	Map<Person,Integer> map = new HashMap<Person, Integer>();
	map.put(new Person("hhm", 12), 1);
	map.put(new Person("yyh", 18), 2);
	map.put(new Person("hyh", 20), 3);
	System.out.println(map.containsKey(new Person("hhm",12)));
}
           

输出:false

输出false是因为底层调用了默认hashcode和equals方法,而默认的方法无法判断是否相等。

重写hashcode和equals方法:

class Person {
	private String name;
	private int age;

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public int hashCode() {
		return this.name.hashCode() ^ (this.age * 10);
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null)
			return false;

		Person p = (Person) obj;
		return this.getName().equals(p.getName()) && this.getAge() == p.age;
	}

}
public class Test1{
    public static void main(String[] args) {
		Map<Person,Integer> map = new HashMap<Person, Integer>();
		map.put(new Person("hhm", 12), 1);
		map.put(new Person("yyh", 18), 2);
		map.put(new Person("hyh", 20), 3);
		System.out.println(map.containsKey(new Person("hhm",12)));
     }
}

           

输出:true

重写hashcode方法要求保证每个对象都是不同的hashcode码

重写equals方法的要求:

1.自反性:对于任何非空引用x,x.equals(x)应该返回true。

2.对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。

3.传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。

4.一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。

5.非空性:对于任意非空引用x,x.equals(null)应该返回false。

2.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}));

3.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.创建只读集合容器:

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

static Collection<String> data = new ArrayList<String>();  
data.add(“test”);  
static Map<String, String> m = new HashMap<String, String>();  
m.put(“key”, “value”);  

//只读集合
Collection<String> c = Collections.unmodifiableCollection(new ArrayList<String>(data));  
System.out.println(c); //可以访问  
//c.add(“test2”);只读,不可添加  

//只读List
List<String> list = Collections.unmodifiableList(new ArrayList<String>(data));  
System.out.println(list.get(0)); //可以访问  
//list.remove(0);只读,不可删除  

//只读Set和Map,和上面相似,使用Collections.unmodifiableList()
           

5.线程同步集合容器:

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>());

6.队列

Queue实现类如下,部分排序不同,性能相似:

public class QueueBehavior {
	private static int count = 10;

	static <T> void test(Queue<T> queue, Generator<T> gen) {
		for (int i = 0; i < count; i++)
			queue.offer(gen.next());
		while (queue.peek() != null)
			System.out.print(queue.remove() + " ");
		System.out.println();
	}

	static class Gen implements Generator<String> {
		String[] s = ("one two three four five six seven " + "eight nine ten")
				.split(" ");
		int i;

		public String next() {
			return s[i++];
		}
	}

	public static void main(String[] args) {
		test(new LinkedList<String>(), new Gen());
		test(new PriorityQueue<String>(), new Gen());
		test(new ArrayBlockingQueue<String>(count), new Gen());
		test(new ConcurrentLinkedQueue<String>(), new Gen());
		test(new LinkedBlockingQueue<String>(), new Gen());
		test(new PriorityBlockingQueue<String>(), new Gen());
	}
} /* Output:
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
*///:~
           

优先级队列PriorityQueue需要通过Comparable比较来排序

LinkedList可做双向队列使用

6.Collections常用方法:

http://www.cnblogs.com/Eason-S/p/5786066.html

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.多条引用路径可及性判断:几条路径中,最强的一条的引用决定对象的可及性。

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

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<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) {  
        int size = 2;  
        //创建存放VeryBig对象的软引用集合  
        LinkedList<SoftReference<VeryBig>> sa = new LinkedList<SoftReference<VeryBig>>();  
        for (int i = 0; i < size; i++) {  
            //将对象和软引用添加到引用队列中  
            sa.add(new SoftReference<VeryBig>(new VeryBig("Soft " + i), rq));  
            System.out.println("Just created: " + sa.getLast());  
            checkQueue();  
        }  
//创建存放VeryBig对象的弱引用集合  
        LinkedList<WeakReference<VeryBig>> wa = new LinkedList<WeakReference<VeryBig>>();  
        for (int i = 0; i < size; i++) {  
            //将对象和弱引用添加到引用队列中  
            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"));  
        //垃圾回收器回收,在回收之前调用对象的finalize()方法  
        System.gc();  
//创建存放VeryBig对象的虚引用集合  
        LinkedList<PhantomReference<VeryBig>> pa = new LinkedList<PhantomReference<VeryBig>>();  
        for (int i = 0; i < size; i++) {  
            //将对象和虚引用添加到引用队列中  
            pa.add(new PhantomReference<VeryBig>(new VeryBig("Phantom " + i),  
                    rq));  
            System.out.println("Just created: " + pa.getLast());  
            checkQueue();  
        }  
    }  
}  
           

 输出结果为:

Just created:[email protected]

Just created:[email protected]

Just created:[email protected]

Just created:[email protected]

In queue: null

Finalizing Weak 0

Finalizing Weak

Finalizing Weak 1

Just created:[email protected]

In queue: null

Just created:[email protected]

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

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

8.WeakHashMap:

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

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

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

继续阅读