Java Map 接口介绍及 HashMap 原理

Map接口中键和值一一映射. 可以通过键来获取值。

  • 给定一个键和一个值,你可以将该值存储在一个Map对象。之后,你可以通过键来访问对应的值。
  • 当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常.
  • 当对象的类型和Map里元素类型不兼容的时候,就会抛出一个 ClassCastException异常。
  • 当在不允许使用Null对象的Map中使用Null对象,会抛出一个NullPointerException 异常。
  • 当尝试修改一个只读的Map时,会抛出一个UnsupportedOperationException异常。
序号 方法描述
1 void clear( )
 从此映射中移除所有映射关系(可选操作)。
2 boolean containsKey(Object k)
如果此映射包含指定键的映射关系,则返回 true。
3 boolean containsValue(Object v)
如果此映射将一个或多个键映射到指定值,则返回 true。
4 Set entrySet( )
返回此映射中包含的映射关系的 Set 视图。
5 boolean equals(Object obj)
比较指定的对象与此映射是否相等。
6 Object get(Object k)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
7 int hashCode( )
返回此映射的哈希码值。
8 boolean isEmpty( )
如果此映射未包含键-值映射关系,则返回 true。
9 Set keySet( )
返回此映射中包含的键的 Set 视图。
10 Object put(Object k, Object v)
将指定的值与此映射中的指定键关联(可选操作)。
11 void putAll(Map m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
12 Object remove(Object k)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
13 int size( )
返回此映射中的键-值映射关系数。
14 Collection values( )
返回此映射中包含的值的 Collection 视图。

实例

下面的例子来解释Map的功能

import java.util.*;

public class CollectionsDemo {

   public static void main(String[] args) {
      Map m1 = new HashMap(); 
      m1.put("Zara", "8");
      m1.put("Mahnaz", "31");
      m1.put("Ayan", "12");
      m1.put("Daisy", "14");
      System.out.println();
      System.out.println(" Map Elements");
      System.out.print("\t" + m1);
   }
}

以上实例编译运行结果如下:

Map Elements
        {Mahnaz=31, Ayan=12, Daisy=14, Zara=8}

延伸:关于HashMap的进一步了解

介绍了HashMap处理冲突的方法,细讲了put()和get()方法,链接如下:

Java HashMap源码解析

个人参考王道书上内容补充一下散列函数相关知识:

散列函数构造方法

在构造散列函数时,必须注意以下几点:

1) 散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围。

2) 散列函数计算出来的地址应该能等概率、均匀地分布在整个地址空间中,从而减少冲突的发生。

3) 散列函数应尽量简单,能够在较短的时间内计算出任一关键字对应的散列地址。

常用的散列函数

1) 直接定址法

字面意思,根据某个线性函数值计算出散列地址,如:

H(key) = a×key + b

优点:简单且不易冲突,适合关键字分布基本连续的情况。

缺点:若关键字分布不连续,空位较多,则会造成存储空间的浪费。

2) 除留余数法(最常用方法)

假定散列列表长m,取一个不大于m但最接近或等于m的质数p,利用以下公式把关键字转换成散列地址。散列函数为:

H(key) = key % p

关键:p的值需要仔细考虑,确保函数转换后等概率地映射到散列空间上的任意地址,从而减少冲突的可能性。

3) 数字分析法

设关键字是r进制数(如十进制数),而r个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会相等;而在某些位上分布不均匀,只有某几种数码经常出现,此时应选取数码分布较为均匀的若干位作为散列地址。

适用场景:适合于已知的关键字集合。

举例:对于公司员工联系方式来说,手机号前几位基本固定几种组合,则可以去后几位作为关键字进行散列存储。

注意:若更换了关键字(如联系方式从手机号变成固定座机),则需要重新构造新的散列函数。

4) 平方取中法

顾名思义,去关键字的平方值的中间几位作为散列地址。具体取多少位要视实际情况而定。

适用场景:关键字的每位取值都不够均匀或均小于散列地址所需的位数时。

举例:1234² = 1,522,756,取中间三位227;2345² = 5,499,025,取中间三位990。

优点:得到的散列地址于关键字的每位都有关系,因此使得散列地址分布比较均匀。

5) 折叠法

将关键字分割成位数相同的几部分(最后一部分的位数可以短一些),然后去这几部分的叠加和作为散列地址。

适用场景:关键字位数很多,且关键字中的每位上数字分布大致均匀时。

举例:1,234,567,890,表长三位,可分为123 | 456 | 789 | 0,叠加求和123 + 456 + 789 + 0 = 1,368,取后三位得368。

处理冲突的方法

想让散列函数绝对地避免冲突是不可能的。为此,需要考虑在发生冲突时如何让关键字寻找下一个“空”的Hash地址。如下图所示:

Java Map 接口介绍及 HashMap 原理_第1张图片

注意:在开放地址的情况下,不能随便删除表中的已有元素,因为若删除元素,则会截断其他具有相同散列地址的元素的查找地址。因此,要删除一个元素时,可给它做一个逻辑标记,进行逻辑删除。但这样做的副作用是:执行多次删除操作之后,表面上看起来散列表很满,但实际上有许多位置未利用,因此需要定期维护散列表,要把删除标记的元素物理删除。

你可能感兴趣的:(概念杂货铺)