java升级(三)集合

集合

概要:

集合类是一个比较大的类常用的ArrayList、HashMap、HashSet,也有不常用的Stack、Queue,有线程安全的Vector、HashTable,也有线程不安全的LinkedList、TreeMap等!

集合的组织关系图

Collection接口:

Collection"是集合类(Collection)的顶级接口,然而”Collections“是一个提供了一系列静态方法的集合工具类,Collection所代表的是一种规则,它所包含的元素都必须遵循一条或者多条规则。如有些允许重复而有些则不能重复、有些必须要按照顺序插入而有些则是散列,有些支持排序但是有些则不支持。在Java中所有实现了Collection接口的类都必须提供两套标准的构造函数,一个是无参,用于创建一个空的Collection,一个是带有Collection参数的有参构造函数,用于创建一个新的Collection,这个新的Collection与传入进来的Collection具备相同的元素。

List接口

继承自Collection的“子接口”如List和Set。List是有序的Collection,即用某种特定的插入顺序维护元素顺序,类似于数组可以按索引访问实现List接口的集合有Arraylist、LinkList、Stack与Vector。以下对这几个集合逐一详细介绍。

ArrayList:

它是实现List接口下一个非同步动态数组,即长度大小可变,元素可以为任意类型包括null,数组有初始容量当增加新元素时会检查容量是否足够,默认初始值为10,可以自定义一个初始容量减少扩容数据拷贝问题,在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

构造函数:

 1)ArrayList():默认构造函数,提供初始容量为10的空列表。2)ArrayList(int initialCapacity):构造一个具有指定初始容量的空列表。3)ArrayList(Collection<? extends E> c):构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

ArrayList操作:

ArrayList底层是数组实现的,所以基本操作都是对数组的操作,private transient Object[] elementData;transient java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。

ArrayList的增加元素函数: add(E e):将指定的元素添加到此列表的尾部。

public boolean add(E e) {
    ensureCapacity(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
    }

先进行扩容操作,然后将列表尾元素指向e;add(int index, E element):将指定的元素插入此列表中的指定位置。

public void add(int index, E element) {
        //判断索引位置是否正确
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(
            "Index: "+index+", Size: "+size);
        //扩容检测
        ensureCapacity(size+1);  
        /*
         * 对源数组进行复制处理(位移),从index + 1到size-index。
         * 主要目的就是空出index位置供数据插入,
         * 即向右移动当前位于该位置的元素以及所有后续元素。 
         */
        System.arraycopy(elementData, index, elementData, index + 1,
                 size - index);
        //在指定位置赋值
        elementData[index] = element;
        size++;
        }

指定位置的插入带来了数组元素的右移,如果大量的插入操作使用Linklist较好。addAll(Collection<? extends E> c):按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
addAll(int index, Collection<? extends E> c):从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。set(int index, E element):用指定的元素替代此列表中指定位置上的元素。

删除元素:

 remove(int index):移除此列表中指定位置上的元素。

public E remove(int index) {
        //位置验证
        RangeCheck(index);
        modCount++;
        //需要删除的元素
        E oldValue = (E) elementData[index];   
        //向左移的位数
        int numMoved = size - index - 1;
        //若需要移动,则想左移动numMoved位
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index,
                    numMoved);
        //置空最后一个元素
        elementData[--size] = null; // Let gc do its work
        return oldValue;
    }

remove(Object o):移除此列表中首次出现的指定元素(如果存在)

public boolean remove(Object o) {
        //因为ArrayList中允许存在null,所以需要进行null判断
        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;
    }

fastRemove()方法用于移除指定位置的元素。

private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
    }

 removeRange(int fromIndex, int toIndex):移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
removeAll():是继承自AbstractCollection的方法,ArrayList本身并没有提供实现。

public boolean removeAll(Collection<?> c) {
        boolean modified = false;
        Iterator<?> e = iterator();
        while (e.hasNext()) {
            if (c.contains(e.next())) {
                e.remove();
                modified = true;
            }
        }
        return modified;
    }

查找: 

ArrayList提供了get(int index)用读取ArrayList中的元素。由于ArrayList是动态数组,根据下标来获取ArrayList中的元素,速度快擅长于随机访问。

扩容:

ensureCapacity()方法扩容,使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量

public void ensureCapacity(int minCapacity) {
        //修改计时器
        modCount++;
        //ArrayList容量大小
        int oldCapacity = elementData.length;
        /*
         * 若当前需要的长度大于当前数组的长度时,进行扩容操作
         */
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            //计算新的容量大小,为当前容量的1.5倍
            int newCapacity = (oldCapacity * 3) / 2 + 1;
            if (newCapacity < minCapacity)
                newCapacity = minCapacity;
            //数组拷贝,生成新的数组
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }

Linklist:

LinkedList是List接口链表的实现。基于链表实现的方式在插入和删除时更优于ArrayList,而随机访问不好。Linklist允许所有元素包括null值。LinkedList继承AbstractSequentialList,实现List、Deque、Cloneable、Serializable。
其增加和移除元素的算法get,remov,insert等可以实现用于栈和队列的使用。与ArrayList一样,非同步的。Linklist有两个基本属性,size和header,size是Linklist的大小,header链表的表头

构造方法

两个构造方法:LinkedList提高了两个构造方法:1) LinkedList()构造一个空列表。里面没有任何元素,仅仅只是将header节点的前一个元素、后一个元素都指向自身。 c)。2)LinkedList(Collection<? extends E> c): 构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。该构造函数首先会调用LinkedList(),构造一个空列表,然后调用了addAll()方法将Collection中的所有元素添加到列表中。

添加:

 add(E e): 将指定元素添加到此列表的结尾。

public boolean add(E e) {
    addBefore(e, header);
        return true;
    }

该方法调用addBefore方法,然后直接返回true,对于addBefore()而已,它为LinkedList的私有方法。

private Entry<E> addBefore(E e, Entry<E> entry) {
        //利用Entry构造函数构建一个新节点 newEntry,
        Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
        //修改newEntry的前后节点的引用,确保其链表的引用关系是正确的
        newEntry.previous.next = newEntry;
        newEntry.next.previous = newEntry;
        //容量+1
        size++;
        //修改次数+1
        modCount++;
        return newEntry;
    }

 add(int index, E element):在此列表中指定的位置插入指定的元素。
addAll(Collection<? extends E> c):添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
addAll(int index, Collection<? extends E> c):将指定 collection 中的所有元素从指定位置开始插入此列表。
AddFirst(E e): 将指定元素插入此列表的开头。
addLast(E e): 将指定元素添加到此列表的结尾。

删除:

remove(Object o):从此列表中移除首次出现的指定元素(如果存在)该方法首先会判断移除的元素是否为null,然后迭代这个链表找到该元素节点,最后调用remove(Entry<E> e),remove(Entry<E> e)为私有方法,是LinkedList中所有移除方法的基础方法。

clear() 从此列表中移除所有元素。
remove()获取并移除此列表的头(第一个元素)。
remove(int index)移除此列表中指定位置处的元素。
remove(Objec o)从此列表中移除首次出现的指定元素(如果存在)。
removeFirst()移除并返回此列表的第一个元素。
removeFirstOccurrence(Object o)从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。
removeLast()移除并返回此列表的最后一个元素。

查找:

就是迭代,比对,然后就是返回当前值。
get(int index):返回此列表中指定位置处的元素。
getFirst():返回此列表的第一个元素。
getLast():返回此列表的最后一个元素。
indexOf(Object o):返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
lastIndexOf(Object o):返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。

HashMap:

定义:

基于哈希表的 Map 接口的实现,以key-value的形式存在,key-value总是会当做一个整体来处理,系统会根据hash算法来来计算key-value的存储位置。继承自AbstractMap。

public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable

构造函数:

1)HashMap():构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。2)HashMap(int initialCapacity):构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。3)HashMap(int initialCapacity, float loadFactor):构造一个带指定初始容量和加载因子的空 HashMap。
初始容量:表示哈希表中桶的容量,加载因子表示哈希表其容量自动增加之前可以达到多满的一种尺度,衡量散列表空间的使用程度。负载因子越大表示散列表的装填程度越高,反之愈小,使用链表法的散列表查找一个元素的平均时间是O(1+a),如果负载因子越大,对空间的利用更充分,后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。系统默认负载因子为0.75,一般情况无需修改。

内部结构:

Hashmap是一个链表散列支持快速存取的结构;Hashmap 的底层是数组,但是数组的每一项都是链表,(HashMap的构造函数)

public HashMap(int initialCapacity, float loadFactor) {
        //初始容量不能<0
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: "
                    + initialCapacity);
        //初始容量不能 > 最大容量值,HashMap的最大容量值为2^30
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //负载因子不能 < 0
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: "
                    + loadFactor);
        // 计算出大于 initialCapacity 的最小的 2 的 n 次方值。
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;     
        this.loadFactor = loadFactor;
        //设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行扩容操作
        threshold = (int) (capacity * loadFactor);
        //初始化table数组
        table = new Entry[capacity];
        init();
    }

initialCapacity代表了该数组的长度,每次新建一个HashMap时会初始化一个table数组。table数组的元素为Entry节点。

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        .......
    }

Entry为HashMap的内部类,它包含了键key、值value、下一个节点next,以及hash值,正是由于Entry才构成了table数组的项为链表。

HashMap的存操作:

存数据源码:

public V put(K key, V value) {
        //当key为null,调用putForNullKey方法,保存null与table第一个位置中,这是HashMap允许为null的原因
        if (key == null)
            return putForNullKey(value);
        //计算key的hash值
        int hash = hash(key.hashCode());                  ------(1)
        //计算key hash 值在 table 数组中的位置
        int i = indexFor(hash, table.length);             ------(2)
        //从i出开始迭代 e,找到 key 保存的位置
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            //判断该条链上是否有hash值相同的(key相同)
            //若存在相同,则直接覆盖value,返回旧value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;    //旧值 = 新值
                e.value = value;
                e.recordAccess(this);
                return oldValue;     //返回旧值
            }
        }
        //修改次数增加1
        modCount++;
        //将key、value添加至i位置处
        addEntry(hash, key, value, i);
        return null;
    }

存储过程是。先判断要保存的数据key是否为null,为null调用putForNullKey方法,不为空计算key的哈希值,根据哈希值搜索数组中索引位置,如果数组中该位置有元素,比较key值知否相同,如果相同覆盖原key下的value(确保没有相同的key),如果不同保存在链头,最先保存的元素在链尾,若数组中没有该元素直接保存。

HashMap的取操作:

public V get(Object key) {
        // 若为null,调用getForNullKey方法返回相对应的value
        if (key == null)
            return getForNullKey();
        // 根据该 key 的 hashCode 值计算它的 hash 码  
        int hash = hash(key.hashCode());
        // 取出 table 数组中指定索引处的值
        for (Entry<K, V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
            Object k;
            //若搜索的key与查找的key相同,则返回相对应的value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

通过key的hash值找到在table数组中的索引处的Entry,然后返回该key对应的value

HashSet

HashSet继承AbstractSet类,实现Set、Cloneable、Serializable接口,底层用HashMap来保存元素。

public class HashSet<E>   
 extends AbstractSet<E>   
 implements Set<E>, Cloneable, java.io.Serializable   
{   
 // 使用 HashMap 的 key 保存 HashSet 中所有元素  
 private transient HashMap<E,Object> map;   
 // 定义一个虚拟的 Object 对象作为 HashMap 的 value   
 private static final Object PRESENT = new Object();   
 ...   
 // 初始化 HashSet,底层会初始化一个 HashMap   
 public HashSet()   
 {   
     map = new HashMap<E,Object>();   
 }   
 // 以指定的 initialCapacity、loadFactor 创建 HashSet   
 // 其实就是以相应的参数创建 HashMap   
 public HashSet(int initialCapacity, float loadFactor)   
 {   
     map = new HashMap<E,Object>(initialCapacity, loadFactor);   
 }   
 public HashSet(int initialCapacity)   
 {   
     map = new HashMap<E,Object>(initialCapacity);   
 }   
 HashSet(int initialCapacity, float loadFactor, boolean dummy)   
 {   
     map = new LinkedHashMap<E,Object>(initialCapacity   
         , loadFactor);   
 }   
 // 调用 map 的 keySet 来返回所有的 key   
 public Iterator<E> iterator()   
 {   
     return map.keySet().iterator();   
 }   
 // 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数  
 public int size()   
 {   
     return map.size();   
 }   
 // 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,  
 // 当 HashMap 为空时,对应的 HashSet 也为空  
 public boolean isEmpty()   
 {   
     return map.isEmpty();   
 }   
 // 调用 HashMap 的 containsKey 判断是否包含指定 key   
 //HashSet 的所有元素就是通过 HashMap 的 key 来保存的  
 public boolean contains(Object o)   
 {   
     return map.containsKey(o);   
 }   
 // 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap   
 public boolean add(E e)   
 {   
     return map.put(e, PRESENT) == null;   
 }   
 // 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素  
 public boolean remove(Object o)   
 {   
     return map.remove(o)==PRESENT;   
 }   
 // 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素  
 public void clear()   
 {   
     map.clear();   
 }   
 ...   
}  

HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。

iterator()方法返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。底层调用HashMap的keySet返回所有的key,这点反应了HashSet中的所有元素都是保存在HashMap的key中,value则是使用的PRESENT对象,该对象为static final。size()返回此 set 中的元素的数量(set 的容量)。底层调用HashMap的size方法,返回HashMap容器的大小。isEmpty(),判断HashSet()集合是否为空,为空返回 true,否则返回false。contains(),判断某个元素是否存在于HashSet()中,存在返回true,否则返回false。更加确切的讲应该是要满足这种关系才能返回true:(o==null ? e==null : o.equals(e))。底层调用containsKey判断HashMap的key值是否为空。add()如果此 set 无此元素则添加此元素。如果此Set没有包含满足(e==null ? e2==null : e.equals(e2)) 的e2时,则将e2添加到Set中,否则不添加且返回false。由于底层使用HashMap的put方法将key = e,value=PRESENT构建成key-value键值对,当此e存在于HashMap的key中,则value将会覆盖原有value,但是key保持不变,所以如果将一个已经存在的e元素添加中HashSet中,新添加的元素是不会保存到HashMap中,所以这就满足了HashSet中元素不会重复的特性。remove如果指定元素存在于此 set 中,则将其移除。底层使用HashMap的remove方法删除指定的Entry。clear从此 set 中移除所有元素。底层调用HashMap的clear方法清除所有的Entry。

HashTable

解析:

HashTable继承Dictionary类,实现Map接口。其中Dictionary类是任何可将键映射到相应值的类的抽象父类。Hashtable中有几个比较重要的参数:1)table:为一个Entry[]数组类型,Entry代表了“拉链”的节点,每一个Entry代表了一个键值对,哈希表的"key-value键值对"都是存储在Entry数组中的。2)loadFactor:加载因子。3)count:HashTable的大小,是指他所包含Entry键值对的数量。不是指容器大小。4)threshold:Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。5)modCount:用来实现“fail-fast”机制的,所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你出错。

构造函数:

1)public Hashtable() {        this(11, 0.75f);    }构造初始容量11,与加载因子0.75的哈希表。2)指定初始容量和默认加载因子 this(initialCapacity, 0.75f);3)public Hashtable(int initialCapacity, float loadFactor)指定两个参数。4)public Hashtable(Map<? extends K, ? extends V> t) {        //设置table容器大小,其值==t.size * 2 + 1        this(Math.max(2*t.size(), 11), 0.75f);        putAll(t);    }  构造一个与给定的 Map 具有相同映射关系的新哈希表。

Hashtable的存与取方法:
 

public synchronized V put(K key, V value) {//向哈希表中添加键值对  
        // Make sure the value is not null  
        if (value == null) {//确保值不能为空  
            throw new NullPointerException();  
        }  
        // Makes sure the key is not already in the hashtable.  
        Entry tab[] = table;  
        int hash = hash(key);//根据键生成hash值---->若key为null,此方法会抛异常  
        int index = (hash & 0x7FFFFFFF) % tab.length;//通过hash值找到其存储位置  
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {/遍历链表  
            if ((e.hash == hash) && e.key.equals(key)) {//若键相同,则新值覆盖旧值  
                V old = e.value;  
                e.value = value;  
                return old;  
            }  
        }  
        modCount++;  
        if (count >= threshold) {//当前容量超过阈值。需要扩容  
            // Rehash the table if the threshold is exceeded  
            rehash();//重新构建桶数组,并对数组中所有键值对重哈希,耗时!  
            tab = table;  
            hash = hash(key);  
            index = (hash & 0x7FFFFFFF) % tab.length;//这里是取摸运算  
        }  
        // Creates the new entry.  
        Entry<K,V> e = tab[index];  
        //将新结点插到链表首部  
        tab[index] = new Entry<>(hash, key, value, e);//生成一个新结点  
        count++;  
        return null;  
    }  

Hasbtable:1)并不允许值和键为空(null),若为空,会抛空指针put方法在开始处仅对value进行判断,并未对key判断,因为当调用hash方法时,若key为空,依然会抛出空指针异常。2)HashMap计算索引的方式是h&(length-1),而Hashtable用的是模运算,效率上是低于HashMap的。3)另外Hashtable计算索引时将hash值先与上0x7FFFFFFF,这是为了保证hash值始终为正数。4)特别需要注意的是这个方法包括下面要讲的若干方法都加了synchronized关键字,也就意味着这个Hashtable是个线程安全的类,这也是它和HashMap最大的不同点.

添加元素流程为:

计算key的hash值根据hash值获得key在table数组中的索引位置,然后迭代该key处的Entry链表,若该链表中存在一个这个的key对象,那么就直接替换其value值即可,否则在将改key-value节点插入该index索引位置处。再添加元素是会进行扩容操作,扩容代码为:

protected void rehash() {  
        int oldCapacity = table.length;//记录旧容量  
        Entry<K,V>[] oldMap = table;//记录旧的桶数组  
        // overflow-conscious code  
        int newCapacity = (oldCapacity << 1) + 1;//新容量为老容量的2倍加1  
        if (newCapacity - MAX_ARRAY_SIZE > 0) {  
            if (oldCapacity == MAX_ARRAY_SIZE)//容量不得超过约定的最大值  
                // Keep running with MAX_ARRAY_SIZE buckets  
                return;  
            newCapacity = MAX_ARRAY_SIZE;  
        }  
        Entry<K,V>[] newMap = new Entry[newCapacity];//创建新的数组  
        modCount++;  
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);  
        boolean currentAltHashing = useAltHashing;  
        useAltHashing = sun.misc.VM.isBooted() &&  
                (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);  
        boolean rehash = currentAltHashing ^ useAltHashing;  
        table = newMap;  
        for (int i = oldCapacity ; i-- > 0 ;) {//转移键值对到新数组  
            for (Entry<K,V> old = oldMap[i] ; old != null ; ) {  
                Entry<K,V> e = old;  
                old = old.next;  
                if (rehash) {  
                    e.hash = hash(e.key);  
                }  
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;  
                e.next = newMap[index];  
                newMap[index] = e;  
            }  
        }  
    }  

Hashtable每次扩容,容量都为原来的2倍加2,而HashMap为原来的2倍。获取代码为:

public synchronized V get(Object key) {//根据键取出对应索引  
      Entry tab[] = table;  
      int hash = hash(key);//先根据key计算hash值  
      int index = (hash & 0x7FFFFFFF) % tab.length;//再根据hash值找到索引  
      for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {//遍历entry链  
          if ((e.hash == hash) && e.key.equals(key)) {//若找到该键  
              return e.value;//返回对应的值  
          }  
      }  
      return null;//否则返回null  
  }  

Hashtable特点

如果你传的参数为null,是会抛空指针。
1)Hashtable是个线程安全的类(HashMap线程不安全);
2)Hasbtable并不允许值和键为空(null),若为空,会抛空指针(HashMap可以);
3)Hashtable不允许键重复,若键重复,则新插入的值会覆盖旧值(同HashMap);
4)Hashtable同样是通过链表法解决冲突;
5)Hashtable根据hashcode计算索引时将hashcode值先与上0x7FFFFFFF,这是为了保证hash值始终为正数;
6)Hashtable的容量为任意正数(最小为1),而HashMap的容量始终为2的n次方。Hashtable默认容量为11,HashMap默认容量为16;
7)Hashtable每次扩容,新容量为旧容量的2倍加2,而HashMap为旧容量的2倍;
8)Hashtable和HashMap默认负载因子都为0.75;

哈希码

集合是分两类List与set,list有序可重复,set无须不重复,再添加元素是每次equals方法判断,在大量元素是会造成效率低下,哈希码是散列算法是将数据依特定算法直接指定到一个地址上,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。Java对于eqauls方法和hashCode方法是这样规定的:
1、如果两个对象相同,那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同。
hashcode与equals的使用
equals()是对两个对象的地址值进行的比较(即比较引用是否相同)。
hashCode()是一个本地方法,它的实现是根据本地机器相关的。
Java语言对equals()的要求如下,这些要求是必须遵循的:
A 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
B 反射性:x.equals(x)必须返回是“true”。
C 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
D 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
equals()相等的两个对象,hashcode()一定相等;反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值