- 浏览: 264132 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (88)
- JAVA / base (26)
- JAVA / web (12)
- JAVA / Lib-tools (5)
- SERVER / tomcat (4)
- DB / mysql (4)
- DB / mongodb (2)
- DB / memcached (2)
- DB / redis (2)
- WEB / Front-end (3)
- WEB / security (4)
- WEB / css (2)
- WEB / js (4)
- OS / linux (3)
- IT / Architecture (4)
- IT / other (2)
- Android (9)
- Go (1)
- Other (1)
- OS / Mac (2)
最新评论
-
Zero2Max:
哈哈,马士兵老师也发现了。
java实现接口的bug -
xly1981:
能像CSRF攻击一样带个图就更棒了
XSS跨站攻击 -
xmong:
df274119386 写道在javascript中看到下面的 ...
CSRF攻击与防御策略 -
df274119386:
在javascript中看到下面的语句 e.value = t ...
CSRF攻击与防御策略 -
xmong:
yzxqml 写道xmong 写道yzxqml 写道tomca ...
Tomcat集群
Hash存储机制
目录
1 HASH存储 1
1.1 HASH存储 1
1.2 集合和引用 1
2 HASHMAP 1
2.1 HASHMAP存储实现 1
2.2 HASHMAP代码实现 2
3 HASHSET 9
3.1 HASHSET代码实现 9
3.2 HASHMAP的PUT与HASHSET的ADD 11
1 Hash存储
1.1 Hash存储
HashMap 和HashSet 是Java Collection Framework 的两个重要成员,其中HashMap是Map 接口的常用实现类,HashSet 是Set 接口的常用实现类。虽然HashMap 和HashSet实现的接口规范不同,但它们底层的Hash 存储机制完全一样,甚至HashSet 本身就采用HashMap 来实现的.通过HashMap、HashSet 的源代码分析其Hash 存储机制.
1.2 集合和引用
就像引用类型的数组一样,当我们把Java 对象放入数组之时,并不是真正的把Java 对象放入数组中,只是把对象的引用放入数组中,每个数组元素都是一个引用变量。
实际上,HashSet 和HashMap 之间有很多相似之处,对于HashSet 而言,系统采用Hash 算法决定集合元素的存储位置,这样可以保证能快速存取集合元素;对于HashMap 而言,系统key-value 当成一个整体进行处理,系统总是根据Hash 算法来计算key-value 的存储位置,这样可以保证能快速存取Map 的key-value 对。
在介绍集合存储之前需要指出一点:虽然集合号称存储的是Java 对象,但实际上并不会真正将Java 对象放入Set 集合中,只是在Set 集合中保留这些对象的引用而言。也就是说:Java 集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的Java 对象。
2 HashMap
2.1 HashMap存储实现
要知道hashmap是什么,首先要搞清楚它的数据结构,在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,hashmap也不例外。Hashmap实际上是一个数组和链表的结合体(在数据结构中,一般称之为“散列存储“),请看下图:横排表示数组,纵排表示数组元素(实际上是一个链表结构)。
从图中看出一个hashmap就是一个数组结构,当新建一个hashmap的时候,就会初始化一个数组,而每个数组元素对应的是一个链表引用。
往hashmap中put元素:
① 先根据key的hash值得到这个元素在数组中的位置(即下标),然后把这个元素放到对应的位置中了。
② 如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。
从hashmap中get元素:
① 首先计算key的hashcode,找到数组中对应位置的某一元素;
② 然后通过key的equals方法在对应位置的链表中找到需要的元素。
从这里我们可以想象得到,如果每个位置上的链表只有一个元素,那么hashmap的get效率将是最高的。
2.2 HashMap代码实现
当程序试图将多个key-value 放入HashMap 中时,以如下代码片段为例:
HashMap<String , Double> map = new HashMap<String , Double>(); map.put("语文" , 80.0); map.put("数学" , 89.0); map.put("英语" , 78.2);
HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置。
当程序执行map.put("语文" , 80.0)时, 系统将调用"语文" 的hashCode()方法得到其hashCode 值——每个Java 对象都有hashCode()方法,都可通过该方法获得它的hashCode值.得到这个对象的hashCode 值之后,系统会根据hashCode 值来决定该元素的存储位置.
我们可以看HashMap 类的put(K key , V value) 方法的源代码:
public V put(K key, V value) { // 如果key 为null,调用putForNullKey 方法进行处理 if (key == null) return putForNullKey(value); // 根据key 的keyCode 计算Hash 值 int hash = hash(key.hashCode()); // 搜索指定hash 值在对应table 中的索引 int i = indexFor(hash, table.length); // 如果i 索引处的Entry 不为null,通过循环不断遍历e 元素的下一个元素 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 找到指定key 与需要放入的key 相等(hash 值相同通过equals 比较放回true) if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 如果i 索引处的Entry 为null,表明此处还没有Entry modCount++; // 将key、value 添加到i 索引处 addEntry(hash, key, value, i); return null; }
上面程序中用到了一个重要的内部接口:Map.Entry,每个Map.Entry 其实就是一个key-value 对。从上面程序中可以看出:当系统决定存储HashMap 中的key-value 对时,完全没有考虑Entry 中的value,仅仅只是根据key 来计算并决定每个Entry 的存储位置。这也说明了前面的结论:我们完全可以把Map 集合中的value 当成key 的附属,当系统决定了key 的存储位置之后,value 随之保存在那里即可。
上面方法提供了一个根据hashCode() 返回值来计算Hash 码的方法:hash(),这个方法
是一个纯粹的数学计算,其方法如下:
static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
对于任意给定的对象,只要它的hashCode() 返回值相同,那么程序调用hash(int h) 方法所计算得到的Hash 码值总是相同的。接下来程序会调用indexFor(int h, int length)方法来计算该对象应该保存在table 数组的哪个索引处。indexFor(int h, int length) 方法的代码如下:
static int indexFor(int h, int length) { return h & (length-1); }
这个方法非常巧妙,它总是通过h &(table.length -1) 来得到该对象的保存位置——而HashMap 底层数组的长度总是2 的n 次方,这一点可参看后面关于HashMap 构造器的介绍。
当length 总是2 的倍数时, h & (length-1) 将是一个非常巧妙的设计: 假设h=5,length=16, 那么h & length - 1 将得到5;如果h=6,length=16, 那么h & length - 1将得到6 ……如果h=15,length=16, 那么h & length - 1 将得到15;但是当h=16 时,length=16 时,那么h & length - 1 将得到0 了;当h=17 时, length=16 时,那么h &length - 1 将得到1 了……这样保证计算得到的索引值总是位于table 数组的索引之内。
根据上面put 方法的源代码可以看出,当程序试图将一个key-value 对放入HashMap中时,程序首先根据该key 的hashCode() 返回值决定该Entry 的存储位置:如果两个Entry 的key 的hashCode() 返回值相同,那它们的存储位置相同。如果这两个Entry 的key 通过equals 比较返回true,新添加Entry 的value 将覆盖集合中原有Entry 的value,但key 不会覆盖。如果这两个Entry 的key 通过equals 比较返回false,新添加的Entry 将与集合中原有Entry 形成Entry 链,而且新添加的Entry 位于Entry链的头部——具体说明继续看addEntry() 方法的说明。
当向HashMap 中添加key-value 对,由其key 的hashCode() 返回值决定该keyvalue对(就是Entry 对象)的存储位置。当两个Entry 对象的key 的hashCode() 返回值相同时,将由key 通过eqauls() 比较值决定是采用覆盖行为(返回true),还是产生Entry 链(返回false)。
上面程序中还调用了addEntry(hash, key, value, i); 代码,其中addEntry 是HashMap提供的一个包访问权限的方法,该方法仅用于添加一个key-value 对.下面是该方法的代码:
void addEntry(int hash, K key, V value, int bucketIndex) { // 获取指定bucketIndex 索引处的Entry Entry<K,V> e = table[bucketIndex]; // ① //将新创建的Entry 放入bucketIndex 索引处,并让新的Entry 指向原来的Entry table[bucketIndex] = new Entry<K,V>(hash, key, value, e); // 如果Map 中的key-value 对的数量超过了极限 if (size++ >= threshold) // 把table 对象的长度扩充到2 倍。 resize(2 * table.length); // ② }
上面方法的代码很简单,但其中包含了一个非常优雅的设计:系统总是将新添加的Entry对象放入table 数组的bucketIndex 索引处——如果bucketIndex 索引处已经有了一个Entry 对象,那新添加的Entry 对象指向原有的Entry 对象(产生一个Entry 链),如果bucketIndex 索引处没有Entry 对象,也就是上面程序①号代码的e 变量是null,也就是新放入的Entry 对象指向null,也就是没有产生Entry 链。
Hash 算法的性能选项
根据上面代码可以看出,在同一个bucket 存储Entry 链的情况下,新放入的Entry 总是位于bucket 中,而最早放入该bucket 中的Entry 则位于这个Entry 链的最末端。
上面程序中还有这样两个变量:
* size:该变量保存了该HashMap 中所包含的key-value 对的数量。
* threshold:该变量包含了HashMap 能容纳的key-value 对的极限,它的值等于HashMap 的容量乘以负载因子(load factor)。
从上面程序中②号代码可以看出,当size++ >= threshold 时,HashMap 会自动调用resize 方法扩充HashMap 的容量。每扩充一次,HashMap 的容量就增大一倍。
上面程序中使用的table 其实就是一个普通数组,每个数组都有一个固定的长度,这个数
组的长度就是HashMap 的容量。HashMap 包含如下几个构造器:
* HashMap():构建一个初始容量为16,负载因子为0.75 的HashMap。
* HashMap(int initialCapacity):构建一个初始容量为initialCapacity,负载因子为0.75 的HashMap。
* HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个HashMap。
当创建一个HashMap 时,系统会自动创建一个table 数组来保存HashMap 中的Entry,下面是HashMap 中一个构造器的代码:
// 以指定初始化容量、负载因子创建HashMap public HashMap(int initialCapacity, float loadFactor) { // 初始容量不能为负数 if (initialCapacity < 0) throw new IllegalArgumentException( "Illegal initial capacity: " + initialCapacity); // 如果初始容量大于最大容量,让出示容量 if (initialCapacity >MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY; // 负载因子必须大于0 的数值 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException(loadFactor); // 计算出大于initialCapacity 的最小的2 的n 次方值。 int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; // 设置容量极限等于容量* 负载因子 threshold = (int)(capacity * loadFactor); // 初始化table 数组 table = new Entry[capacity]; // ① init(); }
上面代码中粗体字代码包含了一个简洁的代码实现:找出大于initialCapacity 的、最小的
2 的n 次方值,并将其作为HashMap 的实际容量(由capacity 变量保存)。例如给定
initialCapacity 为10,那么该HashMap 的实际容量就是16。
initialCapacity 与HashTable 的容量创建HashMap 时指定的initialCapacity 并不等于HashMap 的实际容量,通常来说,HashMap 的实际容量总比initialCapacity 大一些,除非我们指定的initialCapacity 参数值恰好是2 的n 次方。当然,掌握了HashMap 容量分配的知识之后,应该在创建HashMap 时将initialCapacity 参数值指定为2 的n 次方,这样可以减少系统的计算开销。
程序①号代码处可以看到:table 的实质就是一个数组,一个长度为capacity 的数组。
对于HashMap 及其子类而言,它们采用Hash 算法来决定集合中元素的存储位置。当系统开始初始化HashMap 时,系统会创建一个长度为capacity 的Entry 数组,这个数组里可以存储元素的位置被称为“桶(bucket)”,每个bucket 都有其指定索引,系统可以根据其索引快速访问该bucket 里存储的元素。
无论何时,HashMap 的每个“桶”只存储一个元素(也就是一个Entry),由于Entry 对象可以包含一个引用变量(就是Entry 构造器的的最后一个参数)用于指向下一个Entry,因此可能出现的情况是:HashMap 的bucket 中只有一个Entry,但这个Entry 指向另一个Entry ——这就形成了一个Entry 链。HashMap 的读取实现.
当HashMap 的每个bucket 里存储的Entry 只是单个Entry ——也就是没有通过指针产生Entry 链时,此时的HashMap 具有最好的性能:当程序通过key 取出对应value时,系统只要先计算出该key 的hashCode() 返回值,在根据该hashCode 返回值找出该key 在table 数组中的索引,然后取出该索引处的Entry,最后返回该key 对应的value 即可。看HashMap 类的get(K key) 方法代码:
public V get(Object key) { // 如果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; // 搜索该Entry 链的下一个Entry e = e.next) // ① { Object k; // 如果该Entry 的key 与被搜索key 相同 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
从上面代码中可以看出,如果HashMap 的每个bucket 里只有一个Entry 时,
HashMap 可以根据索引、快速地取出该bucket 里的Entry;在发生“Hash 冲突”的情况下,单个bucket 里存储的不是一个Entry,而是一个Entry 链,系统只能必须按顺序遍历每个Entry,直到找到想搜索的Entry 为止——如果恰好要搜索的Entry 位于该Entry 链的最末端(该Entry 是最早放入该bucket 中),那系统必须循环到最后才能找到该元素。
归纳起来简单地说,HashMap 在底层将key-value 当成一个整体进行处理,这个整体就是一个Entry 对象。HashMap 底层采用一个Entry[] 数组来保存所有的key-value对,当需要存储一个Entry 对象时,会根据Hash 算法来决定其存储位置;当需要取出一个Entry 时,也会根据Hash 算法找到其存储位置,直接取出该Entry。由此可见:HashMap 之所以能快速存、取它所包含的Entry,完全类似于现实生活中母亲从小教我们的:不同的东西要放在不同的位置,需要时才能快速找到它。当创建HashMap 时,有一个默认的负载因子(load factor),其默认值为0.75,这是时间和空间成本上一种折衷:增大负载因子可以减少Hash 表(就是那个Entry 数组)所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的的操作(HashMap 的
get() 与put() 方法都要用到查询);减小负载因子会提高数据查询的性能,但会增加Hash
表所占用的内存空间。
掌握了上面知识之后,我们可以在创建HashMap 时根据实际需要适当地调整loadfactor 的值;如果程序比较关心空间开销、内存比较紧张,可以适当地增加负载因子;如果程序比较关心时间开销,内存比较宽裕则可以适当的减少负载因子。通常情况下,程序员无需改变负载因子的值。如果开始就知道HashMap 会保存多个key-value 对,可以在创建时就使用较大的初始化容量,如果HashMap 中Entry 的数量一直不会超过极限容量(capacity * load
factor), HashMap 就无需调用resize() 方法重新分配table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为capacity的Entry 数组),因此创建HashMap 时初始化容量设置也需要小心对待。
3 HashSet
3.1 HashSet代码实现
对于HashSet 而言,它是基于HashMap 实现的,HashSet 底层采用HashMap 来保存所有元素,因此HashSet 的实现比较简单,查看HashSet 的源代码,可以看到如下代码:
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 两个集合在实现本质上是相同的。
3.2 HashMap的put与HashSet的add
由于HashSet 的add() 方法添加集合元素时实际上转变为调用HashMap 的put() 方法来添加key-value 对,当新放入HashMap 的Entry 中key 与集合中原有Entry 的key 相同(hashCode() 返回值相等,通过equals 比较也返回true),新添加的Entry 的value 将覆盖原来Entry 的value,但key 不会有任何改变,因此如果向HashSet 中添加一个已经存在的元素,新添加的集合元素(底层由HashMap 的key 保存)不会覆盖已有的集合元素。
掌握上面理论知识之后,接下来看一个示例程序,测试一下自己是否真正掌握了HashMap和HashSet 集合的功能。
class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } public boolean equals(Object o) { if (this == o) { return true; } if (o.getClass() == Name.class) { Name n = (Name)o; return n.first.equals(first) && n.last.equals(last); } return false; } } public class HashSetTest { public static void main(String[] args) { Set<Name> s = new HashSet<Name>(); s.add(new Name("abc", "123")); System.out.println(s.contains(new Name("abc", "123"))); } }
上面程序中向HashSet 里添加了一个new Name("abc", "123") 对象之后,立即通过程序判断该HashSet 是否包含一个new Name("abc", "123") 对象。粗看上去,很容易以为该程序会输出true。实际运行上面程序将看到程序输出false,这是因为HashSet 判断两个对象相等的标准除了要求通过equals() 方法比较返回true 之外,还要求两个对象的hashCode() 返回值相等。而上面程序没有重写Name 类的hashCode() 方法,两个Name 对象的hashCode() 返回值并不相同,因此HashSet 会把它们当成2 个对象处理,因此程序返回false。
由此可见,当我们试图把某个类的对象当成HashMap 的key,或试图将这个类的对象放入HashSet 中保存时,重写该类的equals(Object obj) 方法和hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的hashCode() 返回值相同时,它们通过equals() 方法比较也应该返回true。通常来说,所有参与计算hashCode()返回值的关键属性,都应该用于作为equals() 比较的标准。hashCode() 和equals()
如下程序就正确重写了Name 类的hashCode() 和equals() 方法,程序如下:
class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } // 根据first 判断两个Name 是否相等 public boolean equals(Object o) { if (this == o) { return true; } if (o.getClass() == Name.class) { Name n = (Name)o; return n.first.equals(first); } return false; } // 根据first 计算Name 对象的hashCode() 返回值 public int hashCode() { return first.hashCode(); } public String toString() { return "Name[first=" + first + ", last=" + last + "]"; } } public class HashSetTest2 { public static void main(String[] args) { HashSet<Name> set = new HashSet<Name>(); set.add(new Name("abc" , "123")); set.add(new Name("abc" , "456")); System.out.println(set); } }
上面程序中提供了一个Name 类,该Name 类重写了equals() 和toString() 两个方法,这两个方法都是根据Name 类的first 实例变量来判断的,当两个Name 对象的first 实例变量相等时,这两个Name 对象的hashCode() 返回值也相同,通过equals()比较也会返回true。
程序主方法先将第一个Name 对象添加到HashSet 中,该Name 对象的first 实例变量值为"abc" , ___________接着程序再次试图将一个first 为"abc" 的Name 对象添加到HashSet 中,很明显,此时没法将新的Name 对象添加到该HashSet 中,因为此处试图添加的Name 对象的first 也是" abc",HashSet 会判断此处新增的Name 对象与原有的Name 对象相同,因此无法添加进入,程序在①号代码处输出set 集合时将看到该集合里只包含一个Name 对象,就是第一个、last 为"123"的Name 对象。__
发表评论
-
Java validation(java验证器实现)
2014-03-18 11:45 3732Java validation 1. java验证器 在 ... -
Memo class备注类信息
2014-03-18 09:52 909Memo Class 1. 什么是Memo Class Mem ... -
java annotation注解
2014-01-24 18:01 9571. Annotation的声明方式 An ... -
Java RMI
2013-03-28 15:12 1744Java Rmi 目录 1 JAVA RMI 1 ... -
java内部类
2013-03-19 16:25 1066Java内部类 目录 1 JAVA ... -
java多线程设计模式之订单模式
2013-03-11 14:00 2716Java多线程实现订单模式: 客户端线程向服务端发起请求后, ... -
java多线程设计模式之线程池处理请求
2013-03-08 17:50 1828Java实现线程池处理请求: 客户端线程发出请求,请求存入请 ... -
java多线程设计模式之异步处理请求
2013-03-08 12:36 4530Java实现多线程异步处理请求: Java实现多线程异步处理 ... -
java多线程设计模式之读写文件模式
2013-03-07 17:56 1608Java实现多线程读写数据 ... -
java多线程设计模式之生产者与消费者
2013-03-07 11:34 1080Java实现多线程生产者与消费者: 生产者线程负责生产产品 ... -
java多线程设计模式之文件保存
2013-03-06 16:16 1631Java实现多线程保存文件:两线程去保存文件,一个保存线程定时 ... -
java多线程设计模式之队列通信
2013-03-06 13:51 2519Java实现多线程处理队列请求通信:客户端线程向请求队列中不断 ... -
Java读linux系统文件文件名乱码
2012-12-06 17:01 91841,问题描述 web应用想通过Java读取linux系统文件显 ... -
Java安全加密
2012-11-28 10:24 2004安全加密 目录 1 加密安全 1 1.1 应用的安全 1 ... -
图着色问题
2012-11-27 13:05 3150图着色问题 目录 1 图 ... -
JDK6新特性
2012-07-03 23:24 2905JDK6的新特性 JDK6的新特性之一_Desktop类 ... -
JDK7新特性
2012-07-03 15:39 3533JDK7新特性 一 JDK7新特性简介 准备 JDK7下载 ... -
JDK5新特性
2012-07-03 10:23 73JDK5.0新特性 1.自动封箱和自动解封(简单类型和封装类 ... -
java多线程
2012-06-15 15:12 1585Java多线程 目录 1 线 ... -
代理模式
2012-06-13 14:12 1397代理模式 目录 1 代理 ...
相关推荐
在计算机科学中,哈希存储机制(Hashing)是一种高效的数据组织方式,它通过特定的哈希函数将数据映射到一个固定大小的数组中,从而实现快速的查找、插入和删除操作。JDK(Java Development Kit)是Java编程语言的...
在Java编程语言中,HashMap和HashSet是两种常用的集合类,它们都依赖于哈希存储机制来提供高效的数据存取性能。这两个类分别实现了Map接口和Set接口,虽然它们的用途不同,但它们底层的实现原理有很强的关联性。本文...
本资源详细介绍了 Java 中的 HashMap 类,包括其实现机制、Hash 存储机制、集合存储机制等方面的知识点。 1. HashMap 和 HashSet 的关系 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,虽然...
在"RocksDB 快速hash存储"这个主题下,我们将深入探讨RocksDB如何实现高效的哈希存储及其背后的优化策略。 ### 1. 哈希表基础 哈希表是一种常用的数据结构,通过哈希函数将键(Key)映射到数组的特定位置来实现...
【基于语义相似性的跨模态图文内容筛选存储机制研究】 随着互联网的快速发展,多媒体数据呈爆炸式增长,尤其在云端存储中,非结构化的多模态数据(如图像和文本)占据了主导地位。传统的存储系统往往侧重于数据的...
1. **哈希函数和哈希表**:uthash中的每个结构体元素都包含一个名为`UT_hash_handle`的隐藏字段,这个字段用于存储哈希值和链表指针。哈希函数用于将结构体的关键字段转换为哈希值,以确定元素在哈希表中的位置。ut...
- **消息认证**是一种验证消息完整性的机制。使用密码学Hash函数产生的消息摘要来确保消息未被篡改。通常,这通过计算消息的哈希值并在发送时包含这个哈希值来实现。 - **消息认证码(MAC)**是使用带密钥的Hash...
5. **线程安全**:虽然UTHash本身并不直接支持线程安全,但源码中提供了一些指导,说明如何在多线程环境中使用UTHash,比如通过自定义锁机制来保护哈希表的读写操作。 总的来说,UTHash是一个简洁、高效的C语言哈希...
本文以《综合管廊中基于区块链驱动的数据存储机制研究》为参考,深入分析了区块链技术在综合管廊数据存储机制中的应用,探索了其如何通过改进的共识算法和加密算法,以及全新的数据交互系统和检索机制,来构建更加...
至于“源码”标签,可能意味着文章深入解析了浏览器内部如何处理哈希值的机制,或者提供了一些自定义的哈希处理函数。而“工具”标签可能暗示了`window.location.hash`在构建工具、辅助库或者开发者调试过程中的一些...
4. **存储和比较**:射手播放器会存储这个哈希值,并在用户尝试播放视频时,重新计算文件的哈希值进行比较。如果两者匹配,说明文件未被篡改,可以安全播放。 标签中的“hash播放器”是指支持使用哈希值进行验证的...
本文档旨在详细阐述内存Hash算法软件模块的设计原理及其实现细节,帮助开发人员更好地理解该模块的工作机制及其应用场景,进而有效地集成到相关项目中。 #### 术语、定义与缩略语 - **术语、定义**:本文档未引入...
UTHash库是一个开源的C语言哈希表实现,它为C程序员提供了一种方便的方式来存储和查找数据。这个库的设计目标是简洁、高效且易于集成到现有的C项目中。通过使用UTHash,开发者可以快速地在C程序中实现动态数据结构,...
1. **复杂数据结构存储**:在需要存储不可比较键的场景下,如存储基于自定义规则的键值对,cznic-hash提供了一种有效解决方案。 2. **数据库索引**:对于一些需要建立复杂索引的数据库系统,cznic-hash可以作为底层...
- 状态管理:哈希可以存储页面的状态,例如用户筛选条件或滚动位置,当页面重新加载时,可以通过读取哈希恢复这些信息。 - 历史记录管理:HTML5的History API允许开发者使用`pushState`和`replaceState`方法管理...
哈希(Hash)算法在信息技术领域中扮演着重要的角色,特别是在数据存储、验证、搜索以及安全加密等方面。本文将深入探讨哈希算法的基本概念、工作原理,并结合VC++编程环境,解析如何在实际项目中应用哈希算法。提供...
内存数据库是一种特殊的数据库系统,它的...在设计和实现内存数据库的Hash索引时,需要综合考虑数据的特性、内存管理、持久化策略、并发控制以及冲突解决机制等多个方面,以实现高效、稳定且适应业务需求的数据库系统。
因此,使用CRC校验机制来定期检测数据完整性是保证系统稳定运行的有效手段。 综上所述,“HASH_EN 校验码检查”工具通过集成哈希算法和CRC32校验两种技术,提供了强大的数据完整性验证方案。这种综合性的工具不仅...