JAVA集合类中的哈希总结

目 录

  1、哈希表

  2、Hashtable、HashMap、ConcurrentHashMap、LinkedHashMap、TreeMap区别

  3、Hashtable、HashMap、ConcurrentHashMap、LinkedHashMap、TreeMap源码分析

  4、一致性哈希算法

  5、transient使用方法

  6、迭代器的强一致和弱一致

  7、总结

一、哈希表

  哈希表,是一种数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

  常用的散列函数方法有:取余数法、平方取中法、线性函数法、随机数法等。常见的解决冲突的方法有:链地址法、开发定址法、建立公共溢出区、多哈希函数法。

  Java中的哈希表,即类Hashtable。它的散列方法采用了除留取余数法;解决冲突的方法采用了链地址法。链地址法使用于频繁的插入和删除的操作类型。

二、Hashtable、HashMap、ConcurrentHashMap、LinkedHashMap、TreeMap区别

  Hashtable是一个包含单向链表的二维数组,其数据结构的数组中是Entry<K,V>存储,entry对象。Hashtable有洁癖,不允许存入其中的key或者value为null。Hashtable是线程安全的,所有的方法均用synchronized修饰,这样在任一时刻,只有一个线程可以写Hashtable,因此,对于频繁写操作的业务逻辑,诸如写excel表等时候,速度会非常慢。

  HashMap是最常用的Map型数据结构,它根据键的hashCode()值存储数据。HashMap允许一个key为null,允许多个value为空,HashMap不支持线程的同步,即可能会出现在同一时刻有多个线程同时写HashMap,会产生数据的不一致。如果在修改代码的过程中,需要给HashMap限制为线程同步的,可以采用Collections.synchronizedMap(map);方法使得HashMap可以同步。

  ConcurrentHashMap是基于这样的考虑:降低锁的粒度。在Hashtable中的关键字是使用synchronized基于整张表结构的,锁的粒度太大,它每次通过锁住整张表让线程独占,来保证安全性。

  LinkedHashMap保存了记录的插入顺序,在使用Iterator遍历LinkedHashMap的时候,先得到的记录肯定是先插入的。在遍历的时候会比HashMap慢,因为HashMap是以O(1)来设计存取的。并且LinkedHashMap继承自HashMap,拥有它的全部特性。

  TreeMap是基于红黑树实现的,它是一种有序的存储结构,并且程序员可以自己定义排序器。TreeMap默认会按存入的键值key来排序,默认是按升序排序,当然也可以指定排序的比较器。TreeMap同样有洁癖,不允许存入null值。使用Iterator遍历出来的TreeMap往往是有序的。

  总结:常用HashMap,允许null插入;有两个子类:ConcurrentHashMap和LinkedHashMap。前者用来弥补线程安全,后者用来弥补有序。此外还有Hashtable和TreeMap。虽然CouncurrentHashMap性能明显优于Hashtable,但是并不能完全取代Hashtable,因为遍历ConcurrentHashMap的迭代器是弱一致的。TreeMap数据结构则可以帮助我们得到一个有序的结果,适用于需要输出排序结果的场景。

三、Hashtable、HashMap、ConcurrentHashMap、LinkedHashMap、TreeMap源码分析

  Hashtable源码如下:

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable { /**
* The hash table data.
*/
private transient Entry<K,V>[] table; /**
* The total number of entries in the hash table.
*/
private transient int count; public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
} /**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
initHashSeedAsNeeded(initialCapacity);
} /**
* Constructs a new, empty hashtable with the specified initial capacity
* and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero.
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
} /**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
} /**
* Constructs a new hashtable with the same mappings as the given
* Map. The hashtable is created with an initial capacity sufficient to
* hold the mappings in the given Map and a default load factor (0.75).
*
* @param t the map whose mappings are to be placed in this map.
* @throws NullPointerException if the specified map is null.
* @since 1.2
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
} /**
* Returns the number of keys in this hashtable.
*
* @return the number of keys in this hashtable.
*/
public synchronized int size() {
return count;
} /**
* Tests if this hashtable maps no keys to values.
*
* @return <code>true</code> if this hashtable maps no keys to values;
* <code>false</code> otherwise.
*/
public synchronized boolean isEmpty() {
return count == 0;
}

Hashtable部分源码

  HashMap等源码不一一列举。

四、一致性哈希算法

一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义:
1、平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。
2、单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
3、分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
4、负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同 的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。

五、transient使用方法

  在Java中一个对象只要实现了Serilizable接口,这个对象就可以被序列化。java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。在开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。示例代码如下:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class TransientTest { public static void main(String[] args) { User user = new User();
user.setUsername("Alexia");
user.setPasswd("123456"); System.out.println("read before Serializable: ");
System.out.println("username: " + user.getUsername());
System.err.println("password: " + user.getPasswd()); try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:/user.txt"));
os.writeObject(user); // 将User对象写进文件
os.flush();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream("C:/user.txt"));
user = (User) is.readObject(); // 从流中读取User的数据
is.close(); System.out.println("\nread after Serializable: ");
System.out.println("username: " + user.getUsername());
System.err.println("password: " + user.getPasswd()); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
} class User implements Serializable {
private static final long serialVersionUID = 8294180014912103005L; private String username;
private transient String passwd; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPasswd() {
return passwd;
} public void setPasswd(String passwd) {
this.passwd = passwd;
} }

TransientTest测试类示范

  java的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

六、迭代器的强一致和弱一致

  fail-fast机制,是Java集合中的一种错误机制。当多个线程对同一个集合中进行操作时,就可能会产生fail-fast事件。当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。

java.util 包中的集合类都返回 fail-fast 迭代器,这意味着它们假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast 迭代器检测到在迭代过程中进行了更改操作,那么它会抛出 ConcurrentModificationException,这是不可控异常。

七、总结

  ConcurrentHashMap是一个线程安全的Map结合,它采用锁分离技术,通过多个锁代替Hashtable中的单个锁(这么说,JDK中先有的hashtable,然后又的ConcurrentHashMap)。ConcurrentHashMap使用了ReentrantLock锁,而不是Sychronized锁。ConcurrentHashMap中的get、put、remove三个方法保证了数据同步,但是没有使用锁。

详细请参考:http://ifeve.com/java-concurrent-hashmap-2/

  具体使用细节,示例代码如下:

import java.util.HashMap;

public class HashTableTest {
public static void main(String args[]) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("weight", "85.4KG");
map.put("height", "180cm");
boolean isexists = map.containsKey("weight");
for (String str : map.keySet()) {
if (isexists) {
System.err.println("name:" + str + ", value:" + map.get(str));
}
}
}
}

keySet()使用

  

  

Java集合类中的哈希总结的更多相关文章

  1. java集合类中的迭代器模式

    不说模式的问题,看一个<<设计模式之禅>>里面的例子. 老板要看到公司了各个项目的情况.(我知道我这个概述很让人头大,看代码吧) 示例程序 v1 package Iterato ...

  2. Java集合类中的Iterator和ListIterator的区别

    注意:内容来自网络他人文章! 最近看到集合类,知道凡是实现了Collection接口的集合类,都有一个Iterator方法,用于返回一个实现了Iterator接口的对象,用于遍历集合:(Iterato ...

  3. Java集合详解8:Java集合类细节精讲

    今天我们来探索一下Java集合类中的一些技术细节.主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充.可能不全面,还请谅解. 本文参考:http://cmsblogs.com/?cat=5 具体 ...

  4. JAVA集合类概览

    带着问题来阅读 1.Java有哪些集合 2.不同集合的应用场景分别是哪些 3.哪些实现类是线程安全的 4.为什么Java集合不能存放基本类型 5.集合的fail-fast和fail-safe是什么 J ...

  5. Java集合类源码解析:AbstractList

    今天学习Java集合类中的一个抽象类,AbstractList. 初识AbstractList AbstractList 是一个抽象类,实现了List<E>接口,是隶属于Java集合框架中 ...

  6. Java集合详解8:Java集合类细节精讲,细节决定成败

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  7. java集合类源码学习三——ArrayList

    ArrayList无疑是java集合类中的一个巨头,而且或许是使用最多的集合类.ArrayList继承自AbstractList抽象类,实现了List<E>, RandomAccess, ...

  8. Java中的哈希

    Java中的哈希 前言 在开发中经常用到HashMap.HashSet等与哈希有关的数据结构,一直只知道这些哈希的数据结构不保证顺序,不清楚具体什么情况.所以在这里大致总结一下.   Java的Has ...

  9. 大话Java中的哈希(hash)结构(一)

    o( ̄▽ ̄)d 小伙伴们在上网或者搞程序设计的时候,总是会听到关于“哈希(hash)”的一些东西.比如哈希算法.哈希表等等的名词,那么什么是hash呢? 一.相关概念 1.hash算法:一类特殊的算法 ...

随机推荐

  1. 拥抱cnpm

    在国内由于墙的原因,使用NPM安装模块经常会失败,要或在速度上会慢得跟蜗牛一样,这时候我们其实可以选择国内淘宝的NPM镜像,使用下面的命令来进行安装: npm install -g cnpm --re ...

  2. Atitit 编程语言知识点tech tree v2 attilax大总结

    Atitit 编程语言知识点tech tree v2 attilax大总结 大分类中分类小分类知识点原理与规范具体实现(javac#里面的实现phpjsdsl(自己实现其他语言实现 类与对象实现对象实 ...

  3. stackView的隐藏与显示注意事项

    1.想要通过变换stackView的y值来让stackView显现/隐藏,同时让其中的button能点击响应,得用topCons 2.导航栏的tinBar要设置为非透明状态

  4. java 模拟qq源码

    java 模拟qq源码: http://files.cnblogs.com/files/hujunzheng/QQ--hjzgg.zip

  5. 通过js获得选择文件的绝对路径

    <form name="thisform" method="get" action="test.jsp" id="thisf ...

  6. 《BI那点儿事》Microsoft 聚类分析算法——三国人物身份划分

    什么是聚类分析? 聚类分析属于探索性的数据分析方法.通常,我们利用聚类分析将看似无序的对象进行分组.归类,以达到更好地理解研究对象的目的.聚类结果要求组内对象相似性较高,组间对象相似性较低.在三国数据 ...

  7. nodejs支持ssi实现include shtml页面

    express 对于include的代码默认不处理,直接输出,没办法执行include的内容,但可以通过Nginx实现. 1. 配置nginx设置开启ssi模式. server { … ssi on; ...

  8. Dijkstra算法(二)之 C++详解

    本章是迪杰斯特拉算法的C++实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法图解 3. 迪杰斯特拉算法的代码说明 4. 迪杰斯特拉算法的源码 转载请注明出处:http://www.cnbl ...

  9. UNIX/Linux打包,压缩 ,解压:

    1.tar打包命令 ①tar cvf tarname.tar file1 file2 $ .log $ .log $ .log $ tar cvf alllog.tar *.log a .log 0K ...

  10. javaccript学习2

    [Javascript]javascript学习 十六 JavaScript 事件 事件是可以被 JavaScript 侦测到的行为. 事件 JavaScript 使我们有能力创建动态页面.事件是可以 ...