Java容器类

@kingpin 2014-06-01 16:29


offer 93

Java容器


set不允许相同值,会将相同集归一

Set:

import java.util.*;

public class SimpleCollection{
public static void main(String[] args) {
    Set<Integer> c = new HashSet<Integer>();
    for(int i=0;i<10;i++){
        c.add(i);
    }
    c.add(0);
    for(Integer i : c){
        System.out.println(i);
    }
}
}

答案:
0 1 2 3 4 5 6 7 8 9

Collection允许相同值

Collection:

import java.util.*;

public class SimpleCollection{
public static void main(String[] args) {
    Collection<Integer> c = new ArrayList<Integer>();
    for(int i=0;i<10;i++){
        c.add(i);
    }
    c.add(0);
    for(Integer i : c){
        System.out.println(i);
    }
}
}

答案:
0 1 2 3 4 5 6 7 8 9 0

Collection的method

import java.util.*;

public class AddingGroups{
public static void main(String[] args) {
    Collection<Integer> collection =  new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
    Integer[] moreInts = {6,7,8,9,10};
    collection.addAll(Arrays.asList(6,7,8,9,10));
    System.out.println(collection.toString());
    Collections.addAll(collection,11,12,13,14,15);
    Collections.addAll(collection,moreInts);
    System.out.println(collection.toString());

}
}

List和set都属于Collection

import java.util.*;

public class ConstructorT{
static String[] FaMovic = {"Shawshank Redemption","Dark Knight Rises","Avengers"};
static int number = FaMovic.length;
static int index = 0;
public static String next(){
    if(index < number){
        return FaMovic[index];
    }
    else{
        index =0;
        return FaMovic[index];
    }
}

public static void main(String[] args) {
    Collection<String> arrListObj = new ArrayList<String>();
    Collection<String> linkListObj = new LinkedList<String> ();
    Collection<String> hashSetObj = new HashSet<String> ();
    Collection<String> linkedHashsetObj = new LinkedHashSet<String> ();
    Collection<String> treeSetObj = new TreeSet<String> ();

    for(int i=0;i<9;i++){
        arrListObj.add(ConstructorT.next());
        linkListObj.add(ConstructorT.next());
        hashSetObj.add(ConstructorT.next());
        linkedHashsetObj.add(ConstructorT.next());
        treeSetObj.add(ConstructorT.next());
        index++;
    }

    System.out.println("arrListObj: "+arrListObj);
    System.out.println("linkListObj: "+linkListObj);
    System.out.println("hashSetObj: "+hashSetObj);
    System.out.println("linkedHashsetObj: "+linkedHashsetObj);
    System.out.println("treeSetObj: "+treeSetObj);

}
}

所有的linked都是按顺序输入,tree是按字典顺序,hash只在乎速度,不在乎顺序

List Integer Arrays.asList(a)

import java.util.*;
public class ListFeaInteger{
public static void main(String[] args) {
        Random rand = new Random(47);
        Integer[] a = {1,2,3,4,5,6,7};
        List<Integer> inte = new ArrayList<Integer>(Arrays.asList(a));
        System.out.println("1: "+inte);
        inte.add(8);
        System.out.println("2: "+inte);
        System.out.println("3: "+inte.contains(8) );
        inte.remove(new Integer(8));
        int temp = inte.get(2);
        System.out.println("4: "+temp +" "+inte.indexOf(temp));
        inte.add(4,new Integer(100));
        System.out.println("5: "+inte);
        List<Integer> sub = inte.subList(1,4);
        System.out.println("subList: "+sub);
        System.out.println("6: "+inte.containsAll(sub));
        Collections.sort(sub);
        System.out.println("sort subList: "+sub);
        Collections.shuffle(sub,rand);
        System.out.println("shuffle subList: "+sub);
    }   
}

List 源码学习分析之:ArrayList

import java.util.*;
import java.util.ArrayList;
public class subL{
public static void main(String[] args) {
    // dog[] arr = { };

    List<Dog> arrListDog = Arrays.asList(new Dog(1), new Dog(2), new Dog(3));
    List<Dog> subLDog = arrListDog.subList(0,2);
    System.out.println(subLDog);
    if(arrListDog.retainAll(subLDog)){
        System.out.println(true);
    }
    System.out.println(arrListDog);
}


}

class Dog{
    int age=0;

    Dog(){age++;}
    Dog(int age){
        this.age = age;
    }

    public String toString(){
        return "dog : "+age;
    }
}

会抛出错误,java.lang.UnsupportedOperationException
进入源码分析
首先Arrays.asList() return 的是:

public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}

ArrayList 泛型ArrayList

而List中是带有retainAll 和 removeAll的

 * Removes from this list all of its elements that are contained in the
 * specified collection (optional operation).
 *
 * @param c collection containing elements to be removed from this list
 * @return <tt>true</tt> if this list changed as a result of the call
 * @throws UnsupportedOperationException if the <tt>removeAll</tt> operation
 *         is not supported by this list
 * @throws ClassCastException if the class of an element of this list
 *         is incompatible with the specified collection (optional)
 * @throws NullPointerException if this list contains a null element and the
 *         specified collection does not permit null elements (optional),
 *         or if the specified collection is null
 * @see #remove(Object)
 * @see #contains(Object)
 */
boolean removeAll(Collection<?> c);

但是从这个注释中哦我们可以看到 @throws UnsupportedOperationException if the removeAll operation 如果没有这个的话会抛出错误

但是 从集成关系中知道

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList是继承了List
但是在ArrayList中是没有removeAll和 retainAll
相当奇怪,现在暂时上不了网,再说吧。这不是违反了Java的实现规则吗?

第二个疑问:

import java.util.*;
public class subL{
public static void main(String[] args) {
    // dog[] arr = { };
//      LinkedList arrLinkedList = (LinkedList)Arrays.asList(new Dog(1), new Dog(2), new Dog(3));

    ArrayList a = new ArrayList();

    LinkedList b= new LinkedList();

    List<Dog> arrListDog = Arrays.asList(new Dog(1), new Dog(2), new Dog(3));
    List<Dog> subLDog = arrListDog.subList(0,2);
    System.out.println(subLDog);
    for(Dog temdog:subLDog)
        arrListDog.remove(temdog);
    System.out.println(arrListDog);
}

}

class Dog{
    int age=0;

    Dog(){age++;}
    Dog(int age){
        this.age = age;
    }

    public String toString(){
        return "dog : "+age;
    }
}

这个情况和刚刚的一样,都是说不支持remove,但是这里的ArrayList有remove方法:

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

也有 removeAll,但是就是报错:错误信息如下:

Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(Unknown Source)
at java.util.AbstractList$Itr.remove(Unknown Source)
at java.util.AbstractCollection.removeAll(Unknown Source)
at subL.main(subL.java:13)

不知道怎么回事。跳过。

各种Collection派生类的应用:

import java.util.*;

public class CrossContainerIteration {
public static void display(Iterator<Dog> it){
    while(it.hasNext()){
        Dog dog = it.next();
        System.out.print(dog+"    ");
    }
    System.out.println();
}

public static void main(String[] args){
    List<Dog> dogs = Arrays.<Dog>asList(new Dog(19),new Dog(21),new Dog(31),new Dog(44),new Dog(6),new Dog(10));

    //      ArrayList<Dog> dogsLs = new ArrayList<Dog>(dogs);

    LinkedList<Dog> dogsll = new LinkedList<Dog>(dogs);

    LinkedHashSet<Dog> dogsLHS = new LinkedHashSet<Dog>(dogsll);

    HashSet<Dog> dogsHs = new HashSet<Dog>(dogs);

    TreeSet<Dog> dogsTs = new TreeSet<Dog>(dogs);

    System.out.println("ArrayList<Dog>");
    display(dogs.iterator());

    System.out.println("LinkedList<Dog>");
    display(dogsll.iterator());

    System.out.println("LinkedHashSet<Dog>");
    display(dogsLHS.iterator());

    System.out.println("HashSet<Dog>");
    display(dogsHs.iterator());

    System.out.println("TreeSet<Dog>");
    display(dogsTs.iterator());
}
}

class Dog implements Comparable<Dog>{

int age=0;

Dog(){age++;}
Dog(int age){
    this.age = age;
}

public String toString(){
    return "dog : "+age;
}

//必须实现compareTo才能被TreeSet使用
public int compareTo(Dog dog){
    if(this.age > dog.age){
        return 1;
    }
    else{
        return -1;
    }
}
}

其中自定义的类必须实现Comparable接口并且实现compareTo才能使用在TreeSet中

Collection类型总结:

LinkedList, ArrayList, HashSet , LinkedHashSet , TreeSet

分析
ArrayList
    private static final Object[] EMPTY_ELEMENTDATA = {};

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //从这句我们可以看出来 newCapacity 是 oldCapacity的1.5倍。
    //结合上下文,最小的增长值也是1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
    // 根据情况来看,elementData才是放元素的,但是他是transitent就是说在传输的时候不会传输这个。
}

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

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    //从这段源码来看,ArrayList可以有null值
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

//看这段挺有感觉的,原来modCount每次在修改的时候都改变是为了在writeObject的时候能同步,如果在writeObject的时候不同步就抛出一个错误。因此,ArrayList线程不安全。
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

小疑问一个:既然此前的 elementData是transient的,为何在writeObject的时候又能使用它?
对一个小问题有兴趣:ArrayList里面的 iterator()返回的是ArrayList里面的一个内部类Itr,然后:

private class Itr implements Iterator<E> {
    /**
     * Index of element to be returned by subsequent call to next.
     */
    int cursor = 0;

    /**
     * Index of element returned by most recent call to next or
     * previous.  Reset to -1 if this element is deleted by a call
     * to remove.
     */
    int lastRet = -1;

    /**
     * The modCount value that the iterator believes that the backing
     * List should have.  If this expectation is violated, the iterator
     * has detected concurrent modification.
     */
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size();
    }

    public E next() {
        checkForComodification();
        try {
            int i = cursor;
            E next = get(i);
            lastRet = i;
            cursor = i + 1;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            //这里指的注意,因为lastRet本来标记的在AbstractList.this.remove(lastRet)中被删除了,所以cursor--指向删除的那个(正常情况下),然后lastRet没地方指了只能回到-1,这种情况下如果再进行 remove,那么AbstractList.this.remove(lastRet)是不会成功的,是不是这样,我们应该试试。
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }

有三种方案删除ArrayList里面的元素,请记着:

import java.util.*;
public class ArrayListItr {
public static void main(String[] args){
    //      Integer[] a = ;
    List<Integer> arr = new ArrayList(Arrays.asList(new Integer[]{1,2,3,4}));
    arr.add(new Integer(1));


    //第三种方案
    ListIterator<Integer> arrListItr = arr.listIterator(arr.size());
    while(arrListItr.hasPrevious()){
        if((new Integer(1).equals(arrListItr.previous()))){
            arrListItr.remove();
        }
    }
    System.out.println(arr);


    //第一种方案
//      Iterator<Integer> it = arr.iterator();
//      while(it.hasNext()){
//          if ((new Integer(1)).equals(it.next())){
//              it.remove();
//          }
//      }
//      System.out.println(arr);


    //第二种方案
    //      for (int i = 0; i < arr.size(); i++) {
    //          if ((new Integer(1)).equals(arr.get(i))) {
    //           arr.remove(i);
    //           i--;
    //          }
    //         }
    //       System.out.println(arr);   
}
}

前文中出现了 由 Arrays.asList引发的错误

现在来使用 listIterator
ListIterator的add方法和Iterator的remove方案注意点一样,将lastRet=1,都是必须 通过 next或者previous将 里面的 lastret值改变,才能继续下一次add和remove

public void add(E e) {
        checkForComodification();

        try {
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
LinkedList

从变量定义的角度看LinkedList就和ArrayList大相径庭

transient int size = 0;

/**
 * Pointer to first node.
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;

存储数据的量原来一直都是 transient,回头不得不好好理解一下transient
我们可以看看Node 的定义:

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

静态内部类,没有空的构造函数

构造函数:

 public LinkedList() {
}

/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 *
 * @param  c the collection whose elements are to be placed into this list
 * @throws NullPointerException if the specified collection is null
 */
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

我们来看看第二个使用Collection的构造函数里面的addAll的内容:

//LinkedList是双向链表
public boolean addAll(int index, Collection<? extends E> c) {
    checkPositionIndex(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
        return false;

    Node<E> pred, succ;
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        //这个是当前的,pred是前一个
        succ = node(index);
        pred = succ.prev;
    }

    for (Object o : a) {
        @SuppressWarnings("unchecked") E e = (E) o;
        Node<E> newNode = new Node<>(pred, e, null);
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        pred = newNode;
    }

    if (succ == null) {
        last = pred;
    } else {
        pred.next = succ;
        succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;
}

//这个函数写得很好,小疑问就是 size>>1不就改变了 size的值了吗?
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}   

这里有个不懂得method

/**
 * Unlinks non-null first node f.
 */
private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

/**
 * Unlinks non-null last node l.
 */
private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}

如果加入的不是头一个,而是第二个,那么释放的就应该是前二个,试一下:
试过后发觉,原来这两个是private,就是只是被内部调用

public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

从remove的源码可以看出来,LinkedList其实是允许null值

public int indexOf(Object o) {
    int index = 0;
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null)
                return index;
            index++;
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item))
                return index;
            index++;
        }
    }
    return -1;
}

在indexOf method中我比较感兴趣的是,空的Object使用equals会返回什么?空的Object equals(null), 试一下:
Object a = new Object();
// Object a = null;
Object b = null;

    a.equals(b);

    System.out.println(a.equals(b));

输出是 false,a != null, b==null, a.equals(b) 是 false,但是如果 a==null,要使用 a.equals() 会报错。

LinkedList还实现了 stack 功能和 deque 接口,能很使用 pollFirst pollLast,push,pop,offerFirst,offerLast等等。
真方便,直接当 stack和deque用。
ArrayList就没有这个功能,但是 LinkedList需要的遍历开销也不小

需要注意的是,LinkedList类本身没有 重写Iterator(),ArrayList重写了Iterator(),所以LinkedList使用的Iterator属于AbstractSequentialList也属于AbstractList源头是AbstractCollection,但是在AbstractCollection中iterator()是个abstract method,所以如果要使用iterator来遍历LinkedList不如直接使用 ListIterator。

同样 在使用 ListIterator的remove或者add前也必须使用 next或者previous

import java.util.*;
public class lrnLinkedlist {
public static void main(String[] args){
    List<Dog> lList= new LinkedList<Dog>(Arrays.asList(new Dog(1),new Dog(2),new Dog(10)));
    LinkedList<Dog> LLList = (LinkedList<Dog>)lList;

//      Iterator<Dog> it = LLList.iterator();

//      ListIterator<Dog> it = LLList.listIterator();
//      
//      it.next();
//      it.remove();
//      it.remove();


    Dog dog1= new Dog(20);


    List<Dog> temp = Arrays.asList(dog1);

    LLList.push(dog1);
    LLList.push(new Dog(11));

    System.out.println(LLList);
    LLList.offerFirst(new Dog(14));

    System.out.println(LLList);
    LLList.pop();

    System.out.println(LLList.containsAll(temp));
    System.out.println(LLList.contains(dog1));

    System.out.println(LLList);
}
}

到这里,LinkedList的基本使用就这样了。

HashSet

欢迎进入HashSet的领域:
看到 HashSet的constructor就震惊了,是HashMap

 private transient HashMap<E,Object> map;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * default initial capacity (16) and load factor (0.75).
 */
public HashSet() {
    map = new HashMap<>();
}

/**
 * Constructs a new set containing the elements in the specified
 * collection.  The <tt>HashMap</tt> is created with default load factor
 * (0.75) and an initial capacity sufficient to contain the elements in
 * the specified collection.
 *
 * @param c the collection whose elements are to be placed into this set
 * @throws NullPointerException if the specified collection is null
 */
public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * the specified initial capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * the specified initial capacity and default load factor (0.75).
 *
 * @param      initialCapacity   the initial capacity of the hash table
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero
 */
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

/**
 * Constructs a new, empty linked hash set.  (This package private
 * constructor is only used by LinkedHashSet.) The backing
 * HashMap instance is a LinkedHashMap with the specified initial
 * capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @param      dummy             ignored (distinguishes this
 *             constructor from other int, float constructor.)
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
// 这个boolean dummy 是用来被LinkedHashSet使用,只是一个生产呢过LinkedHashMap的标识
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

在下面的所有的add,set之类的也是调用HashMap,看来必须先看HashMap

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

原来PRESENT这样用。
HashSet是HashMap里面的key集
有contains,但是不知道有没有indexOf

LinkedHashSet

LinkedHashSet extends HashSet 所有的操作都使用了LinkedHashMap,因此,构造函数有四个

/**
 * Constructs a new, empty linked hash set with the specified initial
 * capacity and load factor.
 *
 * @param      initialCapacity the initial capacity of the linked hash set
 * @param      loadFactor      the load factor of the linked hash set
 * @throws     IllegalArgumentException  if the initial capacity is less
 *               than zero, or if the load factor is nonpositive
 */
public LinkedHashSet(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor, true);
}

/**
 * Constructs a new, empty linked hash set with the specified initial
 * capacity and the default load factor (0.75).
 *
 * @param   initialCapacity   the initial capacity of the LinkedHashSet
 * @throws  IllegalArgumentException if the initial capacity is less
 *              than zero
 */
public LinkedHashSet(int initialCapacity) {
    super(initialCapacity, .75f, true);
}

/**
 * Constructs a new, empty linked hash set with the default initial
 * capacity (16) and load factor (0.75).
 */
public LinkedHashSet() {
    super(16, .75f, true);
}

/**
 * Constructs a new linked hash set with the same elements as the
 * specified collection.  The linked hash set is created with an initial
 * capacity sufficient to hold the elements in the specified collection
 * and the default load factor (0.75).
 *
 * @param c  the collection whose elements are to be placed into
 *           this set
 * @throws NullPointerException if the specified collection is null
 */
public LinkedHashSet(Collection<? extends E> c) {
    super(Math.max(2*c.size(), 11), .75f, true);
    addAll(c);
}
TreeSet

欢迎来到TreeSet的世界
一个路数的东西

/**
 * The backing map.
 */
private transient NavigableMap<E,Object> m;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

NavigableMap
就是说,大概是懂了Map就能把set搞掂。

同时实验证明,Treeset是可以放入相同值,也就是TreeMap的key有相同值。但是Treeset不能放入null,就是TreeMap的key不能为空。大概是要比较所以不能为空。TreeSet 和 TreeMap是如此。TreeSet能够实现相同值,这也说明NavigableMap和HashMap的实现不同, HashMap的内部实现是决定它不能有同一个元素多次出现的原因,看来HashMap的内部结构才是容器的重点。

HashSet 和 LinkedHashSet 都可以为null或者相同值,但是相同值只能出现一次,就是同一个对象放入HashSet和LinkedHashSet中只能存入一次。

但是LinkedList和ArrayList无论是相同值还是null都能够接受。和HashSet,LinkedHashSet不同的是,相同值可以重复出现,在LinkedList和ArrayList的内部实现中,不难理解这个。

欢迎来到HashMap的世界

居然一路写下来,来到了以前载跟头的地方。加油吧

    /**
 * Constructs an empty <tt>HashMap</tt> with the specified initial
 * capacity and load factor.
 *
 * @param  initialCapacity the initial capacity
 * @param  loadFactor      the load factor
 * @throws IllegalArgumentException if the initial capacity is negative
 *         or the load factor is nonpositive
 */
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);

    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    init();
}

/**
 * Constructs an empty <tt>HashMap</tt> with the specified initial
 * capacity and the default load factor (0.75).
 *
 * @param  initialCapacity the initial capacity.
 * @throws IllegalArgumentException if the initial capacity is negative.
 */
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

/**
 * Constructs an empty <tt>HashMap</tt> with the default initial capacity
 * (16) and the default load factor (0.75).
 */
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

/**
 * Constructs a new <tt>HashMap</tt> with the same mappings as the
 * specified <tt>Map</tt>.  The <tt>HashMap</tt> is created with
 * default load factor (0.75) and an initial capacity sufficient to
 * hold the mappings in the specified <tt>Map</tt>.
 *
 * @param   m the map whose mappings are to be placed in this map
 * @throws  NullPointerException if the specified map is null
 */
public HashMap(Map<? extends K, ? extends V> m) {
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                  DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
    inflateTable(threshold);

    putAllForCreate(m);
}

HashMap有四个constructor,float的参数是用来存loadFactor,int 就是 HashMap的初始化容量,下面的还有很多,先放下都不想想了。跑去看Linux。

回到HashMap, 开始吧:

初次在linux平台上写作,感觉还不赖

 private static int roundUpToPowerOf2(int number) {
    // assert number >= 0 : "number must be non-negative";
    return number >= MAXIMUM_CAPACITY
            ? MAXIMUM_CAPACITY
            : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

这段代码是什么意思呢?三目运算符是 右结合性,等价于

   private static int roundUpToPowerOf2(int number) {
    // assert number >= 0 : "number must be non-negative";
    return number >= MAXIMUM_CAPACITY
            ? MAXIMUM_CAPACITY
            :( (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1);
}

当 number 大于1 , number减1 ,最低位为0,左移1位,最低二位为0,那么,number,还是不明白。看来要写测试代码了
Integer 中的 higeestOnebit的源码如下:

/**
 * Returns an {@code int} value with at most a single one-bit, in the
 * position of the highest-order ("leftmost") one-bit in the specified
 * {@code int} value.  Returns zero if the specified value has no
 * one-bits in its two's complement binary representation, that is, if it
 * is equal to zero.
 *
 * @return an {@code int} value with a single one-bit, in the position
 *     of the highest-order one-bit in the specified value, or zero if
 *     the specified value is itself equal to zero.
 * @since 1.5
 */
public static int highestOneBit(int i) {
    // HD, Figure 3-1
    i |= (i >>  1);
    i |= (i >>  2);
    i |= (i >>  4);
    i |= (i >>  8);
    i |= (i >> 16);
    return i - (i >>> 1);
}

这是取出最左段的节奏。

HashMap内部是一个Entry[] ,

这里我们来看看为什么HashMap的null只能有一个:

public V get(Object key) {
    if (key == null)
        return getForNullKey();
    Entry<K,V> entry = getEntry(key);

    return null == entry ? null : entry.getValue();
}

private V getForNullKey() {
    if (size == 0) {
        return null;
    }
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
        if (e.key == null)
            return e.value;
    }
    return null;
}

可以看出 当e.key==null 的时候直接return,从侧面说明e.key==null只有一个,但是,具体还是要到add method才能知道

Entry 节点和 LinkedList里面的 Node相当相似

public final boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry e = (Map.Entry)o;
        Object k1 = getKey();
        Object k2 = e.getKey();
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {
            Object v1 = getValue();
            Object v2 = e.getValue();
            if (v1 == v2 || (v1 != null && v1.equals(v2)))
                return true;
        }
        return false;
    }

从这里可以看出来,当传进来的是 Map.Entry而且 他们的key和alue分别都是相同的,从hashcode或者内存地址上是相同的,这里应该考虑的是alue或者key是String的情况。
HashMap的源码真是又多又难懂,不管了,先试试怎么用:

由于 Arrays也是个很重要的类,所以现在先来分析Arrays:主要的函数有
sort , binarySearch, equals, fill(填充),copyOf,copyOfRange, asList(重点),hashCode(各种类型的hashCode差别还是挺大),deepHashCode(Object a[]),deepEquals(Object[] a1, Object[] a2)(每个都要相同,才返回true),toString, deepToString,

至于toString,deepToString 和 equals deepEquals一个尿性

    int[]  a1 = {1,2,3,4,5};
    int[] a2 = {6,7,8,9,10};

    int[]  b1 = {1,2,3,4,5};
    int[] b2 = {6,7,8,9,10};


    Object[] o1 = {a1,a2};


    System.out.println(Arrays.toString(o1));
    System.out.println(Arrays.deepToString(o1));

输出:
[[I@e69696, [I@a88bc2]
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]


下面研究下 equals和deepEquals的区别:
equals

public static boolean equals(Object[] a, Object[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

    for (int i=0; i<length; i++) {
        Object o1 = a[i];
        Object o2 = a2[i];
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }

    return true;
}

deepEquals

  public static boolean deepEquals(Object[] a1, Object[] a2) {
    if (a1 == a2)
        return true;
    if (a1 == null || a2==null)
        return false;
    int length = a1.length;
    if (a2.length != length)
        return false;

    for (int i = 0; i < length; i++) {
        Object e1 = a1[i];
        Object e2 = a2[i];

        //这一步,如果 e1 和 e2 都是null 就可以,e1!=e2 但是 e1.equals(e2)成立的大概只有String(真的如此,也许Integer也是?试一试),就是说对String Integer, Double, Long 这些Wraper类也是如此。在上面的普通equals中只是需要== 或者equals成立一个就可以了,但是这里,要求他们指向同一个对象。相当复杂。
        if (e1 == e2)
            continue;
        if (e1 == null)
            return false;

        // Figure out whether the two elements are equal
        boolean eq = deepEquals0(e1, e2);

        if (!eq)
            return false;
    }
    return true;
}

static boolean deepEquals0(Object e1, Object e2) {
    assert e1 != null;
    boolean eq;
    if (e1 instanceof Object[] && e2 instanceof Object[])
        eq = deepEquals ((Object[]) e1, (Object[]) e2);
    else if (e1 instanceof byte[] && e2 instanceof byte[])
        eq = equals((byte[]) e1, (byte[]) e2);
    else if (e1 instanceof short[] && e2 instanceof short[])
        eq = equals((short[]) e1, (short[]) e2);
    else if (e1 instanceof int[] && e2 instanceof int[])
        eq = equals((int[]) e1, (int[]) e2);
    else if (e1 instanceof long[] && e2 instanceof long[])
        eq = equals((long[]) e1, (long[]) e2);
    else if (e1 instanceof char[] && e2 instanceof char[])
        eq = equals((char[]) e1, (char[]) e2);
    else if (e1 instanceof float[] && e2 instanceof float[])
        eq = equals((float[]) e1, (float[]) e2);
    else if (e1 instanceof double[] && e2 instanceof double[])
        eq = equals((double[]) e1, (double[]) e2);
    else if (e1 instanceof boolean[] && e2 instanceof boolean[])
        eq = equals((boolean[]) e1, (boolean[]) e2);
    else
        eq = e1.equals(e2);
    return eq;
}

testInteger.java (试一试Integer.equals的效果)

import java.util.*;
public class testInteger {
public static void main(String[] args) {

    Integer abc = new Integer(10);
    Integer aaa = new Integer(10);

//      Integer abc = 130;
//      Integer aaa = 130;

    System.out.println(abc==aaa);
    System.out.println(abc.equals(aaa));

}
}

答案:false,true, 证明了 == 比的是地址,equals比的是hashCode。和String一样.


以下代码可以说明 equals 和 deepEquals之间的区别:

import java.util.*;
public class testInteger {
public static void main(String[] args) {

//      Integer abc = new Integer(10);
//      Integer aaa = new Integer(10);

    int[]  a1 = {1,2,3,4,5};
    int[] a2 = {6,7,8,9,10};

    int[]  b1 = {1,2,3,4,5};
    int[] b2 = {6,7,8,9,10};


    Object[] o1 = {a1,a2};
    Object[] o2 = {b1,b2};

    System.out.println(a1.equals(b1));
    System.out.println(Arrays.equals(o1,o2));
    System.out.println(Arrays.deepEquals(o1, o2));

//      Integer abc = 130;
//      Integer aaa = 130;

//      System.out.println(abc==aaa);
//      System.out.println(abc.equals(aaa));

}
}

答案: flase, false, true

copyOf里面的一个method

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

使用了 Array, 这是反射机制里面的内容:
一直往下面看,最会回到一个native method,至于System.arraycopy也是native method

看完了 Arrays后发觉,构建 还是没有办法构建HashMap,怎么办?
HashMap的键唯一,HashSet也是唯一,HashMap的键的值允许null.

import java.util.*;
public class subL{
    public static void main(String[] args) {

    HashMap hm = new HashMap(DogCollection.getDogMap());

    hm.put(null,new Dog(11));
    hm.put(null,null);

    hm.remove(null);


    System.out.println(hm.get("10"));

    System.out.println(hm.containsValue(DogCollection.getDog1()));

    hm.put("3", new Dog(4));

    Map ma = (Map)hm.clone();

    System.out.println(ma);

    System.out.println(hm.size());

    System.out.println(hm);

    System.out.println(hm.keySet());
    System.out.println(hm.values());

    System.out.println(hm);
    System.out.println(hm.entrySet());


}


}

尽管HashMap的内部实现非常复杂,但是用起来还好,不知道有木有什么陷阱。
对于HashMap的 initialCapacity 和 loadFactor 能正确配置,对提高性能有所影响

下面是 LinkedHashMap
LinkedHashMap不像HashMap , HashMap是使用 Entry[] table 来存数据。
LinkedHashMap使用了Entry Header,accessOrder

//给我20分钟    
//说好的 20 分钟用了 60分钟,下次只能给你6分钟。

LinkedHashMap有5个构造器:
前四个和HashMap一样,最后一个是 加了个accessOrder

accessOrder=true指的是访问顺序
关于HashMap里面的负载因子

利用LinkedHashMap实现的 LRU缓存:

import java.util.*;
public class subL{
    public static void main(String[] args) {


    LinkedHashMap hm = new MyLinkedHashMap(16,0.75f,true);

    hm.putAll(DogCollection.getDogMap());

    System.out.println(hm);

    System.out.println(hm.get("5"));        

    System.out.println(hm);

    hm.put("100", new Dog(100));

    System.out.println(hm);

}
}

class MyLinkedHashMap extends LinkedHashMap{

public MyLinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity,loadFactor,accessOrder);
}

private static final int MAX_ENTRIES = 12;

protected boolean removeEldestEntry(Map.Entry eldest){
    return size()>MAX_ENTRIES;
}
}

Java容器类的学习暂时到这里。






@kingpin 2014-06-01 16:29
正在加载文章图片,请稍后片刻...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值