Java容器类

来源:互联网 发布:决对争锋网络剧资源 编辑:程序博客网 时间:2024/06/06 14:12
@kingpin2014-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.UnsupportedOperationExceptionat 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 Mapprivate 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 Mapprivate 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容器类的学习暂时到这里。






@kingpin2014-06-01 16:29
正在加载文章图片,请稍后片刻...
0 0
原创粉丝点击