读书笔记:《JAVA编程思想(Thinking in Java)》第11章 持有对象(容器)

在写程序时并不知道将需要多少个对象,Java类库提供了一套完整的容器类来解决这个问题。最基本的类型是List、Set、Queue、Map。容器类都可以自动地调整自己的大小,可以将任意数量的对象放置到容器中。

泛型和类型安全的容器

在Java5之前的主要问题是编译器允许你向容器中插入不正确的类型。

import java.util.ArrayList;
class Apple{
	private static long counter;
	private final long id = counter++;
	public long id() { return id;}
}
class Orange{}
public class TestContainer {
    @SuppressWarnings("unchecked")
	public static void main(String[] args) {
		ArrayList apples = new ArrayList();
		for(int i = 0; i < 3; i++)
			apples.add(new Apple());    
		apples.add(new Orange());        //添加时不报错
		for(int i=0; i < apples.size(); i++) 
			((Apple)apples.get(i)).id(); //运行时报错
	}
}

使用泛型后编译时即报错

public class TestContainer {
	public static void main(String[] args) {
		ArrayList<Apple> apples = new ArrayList<>();
		for(int i = 0; i < 3; i++)
			apples.add(new Apple());     
		apples.add(new Orange());       //编译时报错
		for(int i=0; i < apples.size(); i++)
			System.out.println(apples.get(i).id());
	    for(Apple a: apples)
	    	System.out.println(a.id());
	}
}

使用泛型后Apple的子类也可以添加到容器中。

基本概念

在java8源码中List声明为

public interface List<E> extends Collection<E> 	

而Collection接口声明为

public interface Collection<E> extends Iterable<E> 

你可以像下面创建一个List:

List<Apple> apples = new ArrayList<Apple>();

注意,ArrayList已经被向上转型为List,这样使用接口的话如果你决定要修改你的实现,只需要在创建时修改它:

List<Apple> apples = new LinkedList<Apple>();

如果需要用LinkedList独有而List接口中没有的方法,就不能将它向上转型为更通用的接口。

public class TestContainer {
	public static void main(String[] args) {
		Collection<Integer> c = new ArrayList<>();
		for(int i = 0; i < 10; i++)
			c.add(i); //Autoboxing
		for(Integer i : c)
			System.out.print(i + ", ");
	}
}/* Output:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
*///

添加一组元素

在java.util包中的Arrays和Collections类中都有很多实用的方法。
Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数)
Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个逗号分隔的列表,将元素添加到Collection中。

public class TestContainer {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(16,17,18,19,20);
		list.set(1,99);    //OK  -- modify an element
		//list.add(21);    //UnsupportedOperationException
	} 
}

Arrays.asList()方法返回的ArrayList是Arrays类的一个内部类,没有实现add()方法,只重写了set()方法。如果要支持可变,需要

List<Integer> list = new ArrayList<>(Arrays.asList(16,17,18,19,20));

List

ArrayList适用于随机访问元素,但是插入和移除元素较慢
LinkedList通过较低的代价插入和移除元素,但在随机访问方面比较慢
当判断一个元素是否属于一个List,以及移除一个元素都会用到equals()方法。

    //以下来自ArrayList.class源码   
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

迭代器

使用iterator()要求容器返回一个Iterator,next()获得序列中的下一个元素,hasnext()检查序列中是否还有元素,remove()将迭代器新近返回的元素删除。

Set

Set不保存重复的元素,查找成为了Set中最重要的操作,因此你通常都会选择一个HashSet的实现,它专门对快速查找进行了优化。
Set具有与Collection完全一样的接口,因此没有任何额外的功能,只是行为不同。

public interface Set<E> extends Collection<E> 

HashSet输出的顺序没有任何规律所寻,使用了散列。HashSet的add()实现:

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    ...
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    } 
 }

调用了HashMap.class的put函数

public V put(K key, V value) {
	return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

猜你喜欢

转载自blog.csdn.net/weixin_42628594/article/details/82947617