在Java中每一个对象都有一个哈希码,这个值可以通过hashCode()方法获得。hashCode()的值和对象的equals方法息息相关,是两个对象的值是否相等的依据,所以当我们覆盖一个类的equals方法的时候也必须覆盖hashCode方法,HashMap可以看作是Java实现的哈希表。HashMap中存放的是key-value对,对应的类型为java.util.HashMap.Entry,所以在HashMap中数据都存放在一个Entry引用类型的数组table中。这里key是一个对象,为了把对象映射到table中的一个位置,我们可以通过求余法来,所以我们可以使用 [key的hashCode % table的长度]来计算位置(当然在实际操作的时候由于需要考虑table上的key的均匀分布可能需要对key的hashCode做一些处理)。
相关属性 首先肯定是需要一个数组table,作为数据结构的骨干。
transient Entry[] table;
static class Entry<K,V> implements Map.Entry<K,V>{
final K key;
V value;
Entry<K,V> next;
final int hash;
......
这边定义了一个Entry数组的引用。 继续介绍几个概念把
capacity容量 是指数组table的长度
loadFactor 装载因子,是实际存放量/capacity容量 的一个比值,在代码中这个属性是描述了装载因子的最大值,默认大小为0.75
threshold(阈值)代表hashmap存放内容数量的一个临界点,
当存放量大于这个值的时候,就需要将table进行夸张,也就是新建一个两倍大的数组,并将老的元素转移过去。threshold = (int)(capacity * loadFactor);
put方法详解
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
考虑哈希表要添加一个元素,现在假设前提是某个Entry的hash值已经计算出来,那么我们都知道哈希表下一步就是根据这个hash值
把Entry放在table数组的某个位置。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大。我们看一下Java的牛人是怎么写的。Java源码中indexFor(int h, int length) 方法用来计算某Entry对象应该保存在 table 数组的哪个索引处,其代码如下:
static int indexFor(int h, int length) {
return h & (length-1);
}
这个方法简单而又非常巧妙,它通过 h & (table.length -1) 来得到该对象的保存位,而HashMap底层数组的长度总是 2 的 n 次方,这里就是HashMap在速度上优化的一个点。在 HashMap 构造器中有如下代码:
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
这段代码保证初始化时HashMap的容量总是2的n次方,即底层数组的长度总是为2的n次方。
当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
get方法
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
get方法其实就是将key以put时相同的方法算出在table的所在位置,然后对所在位置的链表进行遍历,找到hash值和key都相等的Entry并将value返回。
Map遍历效率问题
//方法一
void printMap(Map<String,Object> map){
Iterator<?> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
@SuppressWarnings("unchecked")
Entry<String,Object> entry = (Entry<String,Object>)iterator.next();
System.out.println("key= "+entry.getKey()+" value= "+entry.getValue());
}
}
//方法二
void printMap1(Map<String,Object> map){
Iterator<String> iterator = map.keySet().iterator();
while(iterator.hasNext()){
Object key = iterator.next();
System.out.println("key= "+key+" value= "+map.get(key) );
}
}
//方法三
void printMap2(Map<String,Object> map){
for(Map.Entry<String, Object> entry:map.entrySet()){
System.out.println("key= "+entry.getKey()+"; value="+entry.getValue());
}
请用方法一和三 , 其实方法二遍历了两遍map 严重影响效率
分享到:
相关推荐
《HashMap源码剖析》 HashMap是Java编程语言中一个非常重要的数据结构,它在实际开发中被广泛应用。作为集合框架的一部分,HashMap实现了Map接口,提供了高效、非同步的键值对存储功能。在这个10页的PDF文档中,...
这篇文章将深入探讨这些类的源码,以帮助我们更好地理解和运用它们。 首先,ArrayList是一个基于数组实现的动态列表,它允许我们在列表的任何位置插入和删除元素。ArrayList的核心是一个Object类型的数组,当我们...
易语言HashMap类是一种在...这些知识点涵盖了易语言HashMap类的基本操作和工作原理,理解和熟练运用这些概念对于高效地使用HashMap类至关重要。通过深入学习和实践,开发者可以更好地利用HashMap类解决实际编程问题。
电话本管理系统是一个常见的应用,它允许...通过理解和运用HashMap的机制,我们可以构建出一个高效、易维护的电话本系统。在实际开发中,了解和掌握HashMap的内部原理对于优化代码性能和解决可能出现的问题至关重要。
- "资料"可能包括HashMap的源码分析,揭示了HashMap如何处理哈希冲突,以及扩容的细节。 - "讲义"可能系统地介绍了HashMap的基础知识,如插入、查找和删除的步骤,以及HashMap与其他数据结构(如TreeMap)的区别。 ...
了解类的构造函数、属性(成员变量)和方法(功能),以及如何通过实例化对象来使用这些类是理解源码的关键。 2. **继承与多态**:Java支持单继承和多态性,这意味着一个类可以继承自另一个类,并且可以通过接口...
然后,我们可以逐个章节地阅读源码,理解每个示例的设计思路,关注如何运用这些基本概念来解决问题。同时,注意观察作者如何处理异常、优化性能以及编写健壮的代码。 对于jar包的使用,我们可以借助Java的命令行...
4. **集合框架**:对ArrayList、LinkedList、HashSet、HashMap等常用集合类的运用,以及泛型的理解和使用。 5. **IO流**:包括文件读写、字符流、字节流、缓冲流等,以及NIO(New IO)的相关操作。 6. **多线程**...
6. **多线程编程**:Java提供了丰富的线程API,源码中可能有对Thread、Runnable接口、synchronized关键字、wait()、notify()方法的运用,帮助理解并发和同步。 7. **网络编程**:Java的Socket编程是实现客户端-...
Java JDK 1.8源码是Java开发人员深入理解Java平台内部工作原理的重要参考资料。...通过深入学习这些源码,我们可以更好地理解和运用Java技术,提高代码质量,同时也能为未来的新版本开发打下坚实基础。
本篇文章将深入探讨"CoreJava"的源码,旨在帮助读者更好地理解和运用Java的基础语法。 1. **对象和类**:Java是一种面向对象的语言,一切皆为对象。在CoreJava中,类是创建对象的蓝图,包含了数据(成员变量)和...
Java编程语言是软件开发领域广泛使用的高级编程语言,尤其在...通过深入学习和分析这个"Java大全源码包",开发者不仅可以提高Java编程技能,还能提升解决问题的能力,更好地理解和运用Java在实际项目中的各种应用场景。
6. **多线程编程**:Java提供了丰富的多线程支持,源码会展示如何创建和管理线程,同步机制如synchronized关键字和wait()、notify()方法的运用。 7. **网络编程**:Java的Socket编程是实现网络应用的基础,源码可能...
其次,这些源码可能涉及到Java集合框架的运用,包括ArrayList、LinkedList、HashMap等数据结构的使用,以及它们在处理数据时的性能比较和优化策略。此外,可能会涉及线程同步和并发控制,如synchronized关键字、wait...
源码分析可以帮助读者理解和运用这些机制,编写出高效、安全的并发程序。 文件I/O操作在任何编程语言中都是必不可少的。Java的File类和InputStream/OutputStream家族提供了丰富的功能,用于读写文件和处理流。通过...
《Java数据结构与算法(第二版)》是一本深度探讨Java编程中数据结构和算法的专著,包含源码分析,旨在帮助读者深入理解并掌握这些核心概念。数据结构是计算机存储、组织数据的方式,而算法则是解决问题或执行任务的...
标签“源码”和“工具”暗示了这是一个关于理解和使用代码的实例,可能包含自定义的工具或功能。在`RandomTest.java`这个文件中,我们可以期待看到一个包含上述逻辑的Java程序。这个程序可能包含以下几个步骤: 1. ...
* 阅读源码可以帮助开发者更好地理解项目中部署和配置相关的核心类开发。 * 阅读源码可以帮助开发者更好地理解 Web 框架的实现。 七、 阅读源码的技巧 * 从 JDK 源代码开始阅读。 * 了解数据结构和算法。 * 了解 ...
以上就是Java源码笔记可能涉及的主要内容,通过深入学习这些知识点,开发者可以更好地理解和运用Java进行网络编程,提高软件开发的效率和质量。同时,对源码的深入理解也有助于解决实际问题,提升编程技能。
在Android开发领域,深入理解和应用源码分析以及开源库的运用是提升技能的关键步骤。"Android+上百实例源码分析以及开源分析+集合打包3"这个资源提供了丰富的学习材料,涵盖了多个方面,旨在帮助开发者更好地理解和...