Java学习笔记(二二)——Java HashMap

【前面的话】

     早上起来好瞌睡哈,最近要注意一样作息状态。

      HashMap好好学习一下。

【定义】

  1. Hashmap是一个散列表,它存储的内容是键值对(key——value)映射。允许null值和null键。
  2. java.lang.Cloneable 接口是一个空接口,该接口用来指明一个对象是否可以进行克隆.实现了该接口的对象可以调用clone()方法来进行对象的浅克隆.  java默认的克隆是浅克隆,浅克隆仅仅克隆所考虑的对象,而不克隆它所引用的对象.
  3. Serializable序列化接口,表明这个类可以进行序列化。
  4. Java HashMap的代码:

         HashMap继承了AbstracMap类,实现了Map接口和Cloneable接口,以及Serializable接口。

     1)  HashMap:

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

     2)  AbstractMap

1 public abstract class AbstractMap<K,V> implements Map<K,V> 

     3)  Map

1 public interface Map<K,V>

     4)  Cloneable

1 public interface Cloneable { //空接口
2 }

     5)  Serializable

1 public interface Serializable {//空接口
2 }

【知识点】

一、HashMap的数据结构

      java中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本的结构来构造的,hashMap也不例外。Hashmap实际上是一个“链接散列”的数据结构,也就是数组和链表的结合体。

      从下图可以看出,HashMap底层是一个数组结构,数组中的每一项又是一个链表,当新建一个HashMap的时候,就会初始化一个数组。

       如下:

      源代码如下:

1 static class Entry<K,V> implements Map.Entry<K,V> {
2         final K key;
3         V value;
4         Entry<K,V> next;
5         int hash;
6 •••
7 }

      可以看出,这就是java泛型的一个使用。就是一个key-value对,并且拥有指向下一个Entry的next,这就构成了一个链表。并且是一个单项链表。

二、HashMap的构造函数

1 public HashMap(int initialCapacity, float loadFactor) 

         指定容器大小和加载因子的构造函数

1 public HashMap(int initialCapacity)

         指定容器大小的构造函数,加载因子默认为0.75

1 public HashMap() 

          默认构造函数,构造一个默认初始容量为16,默认加载因子为0.75的空HashMap

1 public HashMap(Map<? extends K, ? extends V> m) 

          构造一个映射关系与指定Map相同的新的HashMap

三、HashMapAPI

类型

函数和功能

void

clear()

从此映射中移除所有映射关系。

Object

clone()

返回此 HashMap 实例的浅表副本:并不复制键和值本身。

boolean

containsKey(Object key)

如果此映射包含对于指定键的映射关系,则返回 true

boolean

containsValue(Object value)

如果此映射将一个或多个键映射到指定值,则返回 true

Set<Map.Entry<K,V>>

entrySet()

返回此映射所包含的映射关系的 Set 视图。

V

get(Object key)

返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null

boolean

isEmpty()

如果此映射不包含键-值映射关系,则返回   true

Set<K>

keySet()

返回此映射中所包含的键的 Set 视图。

V

put(K key, V value)

在此映射中关联指定值与指定键。

void

putAll(Map<? extends K,? extends V> m)

将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系。

V

remove(Object key)

从此映射中移除指定键的映射关系(如果存在)。

int

size()

返回此映射中的键-值映射关系数。

Collection<V>

values()

返回此映射所包含的值的 Collection 视图。

四、HashMap 的实例有两个参数影响其性能:初始容量加载因子

     1. 容量是哈希表中数组的长度。初始容量只是哈希表在创建时的长度默认为16

1 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    2. 加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。默认加载因子是 0.75,

1  static final float DEFAULT_LOAD_FACTOR = 0.75f;

五、HashMapresizerehash

  1. 为什么会有初始容量和加载因子的设置,原因就是:当HashMap中的元素越来越多的时候,Hash冲突的几率也就会越来越高,也为数组的长度是默认固定的,为16。为了提高查询效率就需要对Hashmap的数组进行扩容,对hashmap进行扩容以后,最消耗性能的地方也就出现了,原数组中的数据必须进行重新计算放到新数组中去。这就是resize。
  2. 默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

六、Hashmap的实现不是同步的,这意味着它不是线程安全的。它的key,value都可以为null。此外,HashMap中的映射不是有序的。

  1. HashMap不是线程安全的意思是多个线程操作的时候,结果可能不可预料,在操作系统中我们学习过,线程的安全状态是绝对安全的,不安全状态是可能会出现不安全的现象,如死锁,但有可能不会出现。
  2. HashMao的映射不是有序的,如下拉链法的例子:

         在有拉链法建立散列表的时候,最终建立的散列表和最初的关键字码中的顺序是不一样的。

         关键字码为:{06,12,15,26,36,38,41,44,51,68},散列函数为H(key)=key%13。用拉链法处理冲突建立的表如下:

【参考资料】

      1. 深入Java集合学习系列:HashMap的实现原理

      2. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例 (推荐这篇文章)

          http://www.cnblogs.com/skywang12345/p/3310835.html

      3. HashMap的工作原理               

          http://www.importnew.com/7099.html

【后面的话】

      好好学习。

——TT

posted @ 2014-04-15 14:10  赞恩  阅读(1287)  评论(0编辑  收藏  举报