基础知识:

数组:

  • 存储连续,占用内存严重;
  • 空间复杂度大,数组的二分查找时间复杂度小,
  • 寻址容易,插入、删除困难;
链表:
  • 存储区间散列,占用内存宽松,空间复杂度小,时间复杂度大 O(N)
  • 寻址困难,插入、删除容易
哈希表:
  •  寻址容易,插入删除也容易;
  •  数组+链表
  •  长度为16的数组,每个元素存储链表的头节结点;
  •  数组的下标:hash(key)%len--元素key的哈希值对数组长度取模;
  • 实现了静态内部类Entry来存值;即Map里面的内容保存在Entry[]里面;
  • 存数据:
key--hash取模Entry长度--index下标--下标对应的值;
  • 取数据
key--hash取模Entry长度--index下标--返回下标对应的值;
  • null key总是存放在Entry[]数组的第一个元素。
哈希冲突:
  • 理想哈希函数:
同一个输入值,产生相同的哈希值;
不同的输入值,产生不同的哈希值;
  • 哈希冲突:
不同的输入值,产生相同的哈希值;
 
SQL Server内置的三个哈希函数
校验和函数:
checksum;
binary_checksum
HashBytes
注意:业务逻辑要求不允许有误差,不要使用任何的哈希函数;只要是哈希函数,就会存在冲突;
 //强烈建议不要在项目中使用校验和函数
//binary_checksum 和 checksum 产生的校验和的质量比较低,建议不要再项目中使用校验函数。当字符串比较短时,很大可能产生Hash 冲突
 
解决哈希冲突的方法:
 

解决hash冲突的办法

  1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
  2. 再哈希法
  3. 链地址法
  4. 建立一个公共溢出区
Java中hashmap的解决办法就是采用的链地址法。
 
具体如下:

创建哈希表和查找哈希表都会遇到冲突,两种情况下解决冲突的方法应该一致。下面以创建哈希表为例,说明解决冲突的方法。常用的解决冲突方法有以下四种:

1.开放定址法

这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。

2. 再哈希法

    这种方法是同时构造多个不同的哈希函数:

    Hi=RH1(key)  i=1,2,…,k

当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

3. 链地址法

    这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。

4.建立公共溢出区

这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

 
 
 

Map :

基于键-值映射的关系来搭建存储结构,在整个结构中使用key值来唯一标识对象.(在JDK1.2之后出现用于替换原有API中的Dictionary类的作用)。常用方法:                                   

                     put(key,value)

                       get(key)

 

                       remove()

 

                       values():将结构中存储的所有值都以对象的形式返回

 

                       keySet():将结构中的所有key值,以set结构返回.

 

 实现类:

 HashMap

  TreeMap: –底层基于二叉树结构来存储数据,会根据key来作为二叉树的节点关键属性来进行排序操作,按key值的从小到大进行排列

1.1.1.1. Map集合阐述

//遍历map

for (Entry<String, Integer> entry : hm.entrySet()) {

System.out.println("name:"+entry.getKey()+"------"+"age:"+entry.getValue());

}

Map接口不是Collection接口的继承。Map接口用于维护键/值对(key/value pairs)。该接口描述了从不重复的键到值的映射。

  (1) 添加、删除操作:
  Object put(Object key, Object value): 将互相关联的一个关键字与一个值放入该映像。如果该关键字已经存在,那么与此关键字相关的新值将取代旧值。方法返回关键字的旧值,如果关键字原先并不存在,则返回null
  Object remove(Object key): 从映像中删除与key相关的映射
  void putAll(Map t): 将来自特定映像的所有元素添加给该映像
  void clear(): 从映像中删除所有映射
  “键和值都可以为null。但是,您不能把Map作为一个键或值添加给自身。”
  (2) 查询操作:
  Object get(Object key): 获得与关键字key相关的值,并且返回与关键字key相关的对象,如果没有在该映像中找到该关键字,则返回null
  boolean containsKey(Object key): 判断映像中是否存在关键字key
  boolean containsValue(Object value): 判断映像中是否存在值value
  int size(): 返回当前映像中映射的数量
  boolean isEmpty() :判断映像中是否有任何映射
  (3) 视图操作 :处理映像中键/值对组
  Set keySet(): 返回映像中所有关键字的视图集
  “因为映射中键的集合必须是唯一的,您用Set支持。你还可以从视图中删除元素,同时,关键字和它相关的值将从源映像中被删除,但是你不能添加任何元素。”
  Collection values():返回映像中所有值的视图集
  “因为映射中值的集合不是唯一的,您用Collection支持。你还可以从视图中删除元素,同时,值和它的关键字将从源映像中被删除,但是你不能添加任何元素。”
  Set entrySet(): 返回Map.Entry对象的视图集,即映像中的关键字/值对
  “因为映射是唯一的,您用Set支持。你还可以从视图中删除元素,同时,这些元素将从源映像中被删除,但是你不能添加任何元素。”
  Map.Entry接口
  Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。集合中每个对象都是底层Map中一个特定的键/值对。

通过这个集合的迭代器,您可以获得每一个条目(唯一获取方式)的键或值并对值进行更改。当条目通过迭代器返回后,除非是迭代器自身的remove()方法或者迭代器返回的条目的setValue()方法,其余对源Map外部的修改都会导致此条目集变得无效,同时产生条目行为未定义。
  (1) Object getKey(): 返回条目的关键字
  (2) Object getValue(): 返回条目的值
  (3) Object setValue(Object value): 将相关映像中的值改为value,并且返回旧值

补充:Entry是Map中用来保存一个键值对的,而Map实际上就是多个Entry的集合。Entry是Map实现类的内部类。看下HashMap实现源码,就明白Entry的作用了。

 

posted on 2018-07-11 20:06  爱迪生是个天使  阅读(103)  评论(0编辑  收藏  举报