Java 7源码分析第7篇 - Java集合

1、关于集合的两道面试题:


先来看几道题目:

1、创建一个不可变的的集合:

      public static void main(String[] args) {
		Set<String> set = new HashSet<String>();
		set.add("Java");
		set.add("JEE");
		set.add("Spring");
		set.add("Hibernate");
		set = Collections.unmodifiableSet(set);
		set.add("Ajax"); // not allowed.
	}
可以看到,创建不可变集合主要是调用了Collections的unmodifiableSet()方法,而Collections类通过装饰模式实现了对一般集合的封装。

2、去除List集合中的重复元素,且保持原有的顺序

public static void main(String args[]) {
        List<String> list=new ArrayList();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("A");
        // List中允许元素重复
        for(int i=0;i<list.size();i++) 
               System.out.print(" "+list.get(i));// A B C A
        // 去除重复的元素且保持原有元素的顺序
        List list2=new testD().function(list);
        for(int i=0;i<list2.size();i++) 
               System.out.print(" "+list2.get(i));// A B C 
        
}
public <e> List<e> function (List<e> list) {
         return new ArrayList<e>(new LinkedHashSet<e>(list));
}

上面的代码代码通过把原有列表传入一个LinkedHashSet来去除重复的元素。在这个情况里,LinkedHashSet可以保持元素原来的顺序。如果这个顺序是不需要的话,那么上面的LinkedHashSet可以用HashSet来替换。


2、集合框架类图及集合对比:


其实如上的两道题目并不难,只是没有对集合了解的更深。下面就来大概的看一下集合吧。集合的继承类如下所示。


大概能够分为三种不同的集合,List、Set和Map,还有一些辅助的工具类,像Collections、Arrays等,各个集合的比较情况如下图。


其实对于集合的操作,无外乎就是

增加:向一个集合中添加一个元素、将一个集合中的所有元素插入到当前集合中

删除:删除指定的一个元素、删除集合中所有的元素

修改:修改指定的一个元素

查找:查当前集合的大小、查找两个集合中的共同元素等

其它:如将一个集合中的元素转换为数组表示形式


3、Iterable、Iterator、Collection接口:


从Java集合框架图可以看出,所有的类都直接或间接继承了Iterable接口,源代码如下:

// Implementing this interface allows an object to be the target of  the "foreach" statement.
public interface Iterable<T> {
    Iterator<T> iterator();
}

接口中定义了一个迭代器,可以使用增强的for循环(foreach)对集合进行遍历。Iterator类的源代码如下:

/**
 * Iterator takes the place of Enumeration. Iterators differ from enumerations in two ways:
 *
 *      <li> Iterators allow the caller to remove elements from the
 *           underlying collection during the iteration with well-defined
 *           semantics.
 *      <li> Method names have been improved.
 */
public interface Iterator<E> {
    boolean hasNext();
    E next();
    /**
     * Removes from the underlying collection the last element returned
     * by this iterator (optional operation).  
     */
    void remove();
}

继续看Collection接口,源代码如下:

public interface Collection<E> extends Iterable<E> {
    // 查找操作
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    boolean containsAll(Collection<?> c);
    // 增加操作
    boolean add(E e);
    boolean addAll(Collection<? extends E> c);
    // 删除操作
    boolean remove(Object o);
    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c);
    void clear();
    
    // 集合转换
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);
    // 提供equals()和hashCode()
    boolean equals(Object o);
    int hashCode();
}
这个接口是专门针对集合定义了各种操作,他定义了常用集合的一般操作,但是由于抽象层次较高,所以一般一个具体的集合实现类,如ArrayList、HashMap等都不会直接继承这个接口,而是继承这个接口的一些子类来实现。所以说每个集合的具体实现类都直接或间接继承了这个接口。

4、List所依赖的接口及抽象类:


查找集合框架图后可知,具体的List实现类继承了AbstractCollection抽象类并且实现了List接口。List接口中又定义了哪些操作呢?

public interface List<E> extends Collection<E> {
    // Query Operations

    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);


    // Modification Operations
    boolean add(E e);
    boolean remove(Object o);

    // Bulk Modification Operations

    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean addAll(int index, Collection<? extends E> c);

    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c);
    void clear();


    // Comparison and hashing
    boolean equals(Object o);
    int hashCode();


    // Positional Access Operations
    // Returns the element at the specified position in this list.
    E get(int index);
    E set(int index, E element);// 修改指定位置的元素

    void add(int index, E element);

    E remove(int index);


    // Search Operations
    int indexOf(Object o);
    int lastIndexOf(Object o);


    // List Iterators
    ListIterator<E> listIterator();
    ListIterator<E> listIterator(int index);

    // View
    List<E> subList(int fromIndex, int toIndex);
}

如上的一些方法都在Collection中定义过的,这里完全可以省略,只是为了明了,同时这个接口中还定义了一些更为具体的方法,这些方法是针对List类型的集合而设定的。这样,Map集合就可以只继承Collection,而不用实现List集合中的方法。所以说,如果在实际应用中,只针对List集合,则完全可以使用List接口,因为这个细化的接口中可以看到许多针对List而定义的方法。如果不确定集合类型,或者是需要List集合与Map集合互操作,那就需要使用Collection接口了。

对于List遍历操作又定义出了更加细化的一个迭代接口,这个接口继承了Iterator接口,如下:

public interface ListIterator<E> extends Iterator<E> {
    // Query Operations
    boolean hasNext();
    E next();
    boolean hasPrevious();
    E previous();
    int nextIndex();
    int previousIndex();
    void remove();
    void set(E e);
    void add(E e);
}

这个接口中增加了不少针对List集合的遍历查找方法。在List接口中使用ListIterator,类似与AbstractCollection使用Iterator一样。

/**
 * This class provides a skeletal implementation of the <tt>Collection</tt>
 * interface, to minimize the effort required to implement this interface. <p>
 */
public abstract class AbstractCollection<E> implements Collection<E> {
    // protected类型的,以便子类能够顺利调用
    protected AbstractCollection() {    }

    public abstract Iterator<E> iterator();// 定义一个iterator的抽象方法
    public abstract int size();

    public boolean isEmpty() {
        return size() == 0;
    }

    public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }
    public boolean remove(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext()) {
                if (it.next()==null) {
                    it.remove();
                    return true;
                }
            }
        } else {
            while (it.hasNext()) {
                if (o.equals(it.next())) {
                    it.remove();
                    return true;
                }
            }
        }
        return false;
    }
    
    public Object[] toArray() {
        // Estimate size of array; be prepared to see more or fewer elements
        Object[] r = new Object[size()];
        Iterator<E> it = iterator();
        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) // fewer elements than expected
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }

    public <T> T[] toArray(T[] a) {
        // Estimate size of array; be prepared to see more or fewer elements
        int size = size();
        T[] r = a.length >= size ? a :
                  (T[])java.lang.reflect.Array
                  .newInstance(a.getClass().getComponentType(), size);
        Iterator<E> it = iterator();

        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) { // fewer elements than expected
                if (a != r)
                    return Arrays.copyOf(r, i);
                r[i] = null; // null-terminate
                return r;
            }
            r[i] = (T)it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }
    // 定义分配数组最大的栈尺寸,防止溢出
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Reallocates the array being used within toArray when the iterator
     * returned more elements than expected, and finishes filling it from
     * the iterator.
     */
    private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
        int i = r.length;
        while (it.hasNext()) {
            int cap = r.length;
            if (i == cap) {
                int newCap = cap + (cap >> 1) + 1;
                // overflow-conscious code
                if (newCap - MAX_ARRAY_SIZE > 0)
                    newCap = hugeCapacity(cap + 1);
                r = Arrays.copyOf(r, newCap);
            }
            r[i++] = (T)it.next();
        }
        // trim if overallocated
        return (i == r.length) ? r : Arrays.copyOf(r, i);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError
                ("Required array size too large");
        return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }

    // Modification Operations
   
    // 省略  ...

    
    //  String conversion
    /**
     * Returns a string representation of this collection.  The string
     * representation consists of a list of the collection's elements in the
     * order they are returned by its iterator, enclosed in square brackets
     * ("[]").  Adjacent elements are separated by the characters  * ", " (comma and space). 
     */
    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

}
从如上的两个方法contains()和remove()方法可以看出,其具体的子类如果继承了这两个方法,那么集合中就可以添加null元素,例如:
ArrayList  aList=new ArrayList();
		aList.add("a");
		aList.add("b");
		aList.add("d");
		aList.add(null);// ArrayList中允许有null值
		System.out.println(aList.size());//4
		
		Iterator it=aList.listIterator();
		while(it.hasNext()){
			System.out.println(it.next());// a b d null
		}
可以看到,在ArrayList集合中完全将null看作一种集合元素进行处理的,但是如果集合中只有一个null元素的时候,调用具体实现类的isEmpty()方法后返回值将为false。由于Set集合也继承了如上的抽象方法,所以这个性质与List类似。

5、Set所依赖的接口及抽象类:


Set接口继承了Collection,其中也定义了一些针对Set集合定义的一些方法,可以看到,与List接口中定义的方法一样。

public interface Set<E> extends Collection<E> {
    // Query Operations
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();

    <T> T[] toArray(T[] a);
    // Modification Operations
    
    boolean add(E e);
    boolean remove(Object o);

    // Bulk Operations
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean retainAll(Collection<?> c);
    boolean removeAll(Collection<?> c);
    void clear();

    // Comparison and hashing
    boolean equals(Object o);
    int hashCode();
}
如上接口中定义的方法都一样,只是实现不一样。但是这样分开为List和Set等集合定义接口的好处就是,如果针对某一个具体集合的操作有一个特殊的操作,那么只在这个接口中定义即可,其他集合不会受到任何影响。

6、Map所依赖的接口及抽象类:



























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值