@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