equals底层代码

1.思考:equals代码怎么执行的?

package equals;

public class Test {

	public static void main(String[] args) {
		String name1="Tom";
		String name2="Tom";
		System.out.println(name1==name2);      //true
		System.out.println(name1.equals(name2));
	   }
}

2.equals底层代码

 public boolean equals(Object anObject) {         //name2传入equals方法,anObject指向name2对象,name2为上转型对象
	        if (this == anObject) {        //因为name1与name2都是采用直接赋值的方式创建的String类型的对象,所以是true
	            return true;
	        }
	        if (anObject instanceof String) {
	            String anotherString = (String)anObject;    //下转型
	            int n = value.length;
	            if (n == anotherString.value.length) {
	                char v1[] = value;
	                char v2[] = anotherString.value;
	                int i = 0;
	                while (n-- != 0) {
	                    if (v1[i] != v2[i])
	                        return false;
	                    i++;
	                }
	                return true;      //将字符串一个一个比较
	            }
	        }
	        return false;
	     }

2.思考:equals代码怎么执行的?

package equals;

public class Test {

        public static void main(String[] args) {
		String name1="Tom";
		String name2=new String("Tom");
		System.out.println(name1==name2);//false
		System.out.println(name1.equals(name2));
		}
	}

分析equals底层代码:

 public boolean equals(Object anObject) {                    //name2为上转型对象
	        if (this == anObject) {            //因为name1与name2地址不同,所以走接下来的代码
	            return true;
	        }
	        if (anObject instanceof String) {         //因为anObject参数指向name2对象,name2为String类型对象,所以anObject instanceof String  是 true  实例 intanceof 类
	            String anotherString = (String)anObject;           //因为anObject参数指向name2对象,name2为String类型对象,所以可以下转型
	            int n = value.length;        //因为value是全局变量,实质是this.value.length,name1 调用 equals,this即为name1,name1就是全局变量,this.value.length就是name1的字符有多少个
	            if (n == anotherString.value.length) {                      //anotherString就是指向name2,anotherString.value.length代表name2有多少个字符。两个字符串相同,则长度一定相同,所以n == anotherString.value.length非常重要
	                char v1[] = value;//由name1字符组成的数组
	                char v2[] = anotherString.value;//由name2字符组成的数组
	                int i = 0;
	                while (n-- != 0) {             //逐个字符比对
	                    if (v1[i] != v2[i])              //只要有一个比对失败则两个字符串一定不相同
	                        return false;
	                    i++;
	                }
	                return true;
	            }
	        }
	        return false;
	     }

3.思考:equals代码怎么执行的?

package equals;

public class Test {

        public static void main(String[] args) {
         String name1="Tom";
		Test test= new Test();
		System.out.println(name1==test);//因为javac在编译时已经知道连个类型不相同,所以编译出错
		System.out.println(name1.equals(test));
		}
	}

分析equals底层代码

 public boolean equals(Object anObject) {      //test传入equals方法,anObject指向test对象,test为上转型对象
	        if (this == anObject) {             //因为name1与test地址不同, 所以走下面的代码
	            return true;
	        }
	        if (anObject instanceof String) {                 //因为anObject参数指向test对象,test为Test类型对象,不是String类,所以anObject instanceof String 直接走到最后    return false;
	            String anotherString = (String)anObject;
	            int n = value.length;
	            if (n == anotherString.value.length) {
	                char v1[] = value;
	                char v2[] = anotherString.value;
	                int i = 0;
	                while (n-- != 0) {
	                    if (v1[i] != v2[i])
	                        return false;
	                    i++;
	                }
	                return true;
	            }
	        }
	        return false;
	     }
<think>我们正在讨论Java中的HashTable。根据引用,HashTable是线程安全的,使用synchronized关键字实现同步。底层是数组+链表结构。不允许null键和null值。 接下来,我将按照以下步骤组织回答: 1. HashTable的基本介绍(包括线程安全特性) 2. 底层数据结构(数组+链表) 3. 主要方法分析(构造方法、put方法、get方法等) 4. 性能与线程安全讨论 5. 相关问题 注意:由于用户要求理解底层实现代码,我们将深入源码分析,但会避免过于冗长的代码片段,而是解释关键步骤。 根据引用[1]和[2]:HashTable是古老的Map实现,线程安全,不允许null键值,底层数组+链表。 引用[4]和[5]提到了同步机制:使用synchronized方法,因此效率较低。 现在开始分析源码(基于Java 8,但HashTable在后续版本中变化不大): 注意:HashTable在Java中已经被认为是遗留类,通常建议使用ConcurrentHashMap或Collections.synchronizedMap来实现同步。 一、基本结构 1. 数组:Entry<?,?>[] table; 2. 链表结构:Entry是一个静态内部类,包含hash, key, value, next。 二、构造方法 默认构造方法:创建初始容量为11,负载因子0.75的哈希表。 三、put方法 步骤: 1. 检查value是否为null,如果是则抛出NullPointerException(根据引用[2]和[4],不允许null值)。 2. 计算key的哈希值(注意:HashTable直接使用key.hashCode(),而HashMap中进行了扰动处理)。 3. 计算索引:index = (hash & 0x7FFFFFFF) % table.length;(确保索引为正) 4. 遍历链表,如果找到相同的key(hash相同且equals为true),则替换旧值并返回旧值。 5. 如果没有找到,则调用addEntry方法添加新节点。 6. addEntry方法中,如果当前元素数量超过阈值(容量*负载因子),则先进行rehash(扩容为原来的2倍+1),然后重新计算索引。 7. 创建新节点,插入到链表的头部(头插法)。 四、get方法 步骤: 1. 计算key的哈希值。 2. 计算索引。 3. 遍历链表,找到相同的key则返回value,否则返回null。 五、rehash方法 1. 新容量为旧容量的2倍+1。 2. 创建新数组。 3. 遍历旧数组的所有链表,重新计算每个节点的索引(index = (hash & 0x7FFFFFFF) % newCapacity)。 4. 将节点插入到新数组的对应位置(头插法)。 注意:所有公共方法(如put, get, remove等)都使用了synchronized修饰,因此是线程安全的。 下面我们用代码片段来展示关键部分(按照要求,代码块使用单独格式,行内公式用$...$):</think>### HashTable底层实现原理及源码分析 HashTable是Java中古老的线程安全Map实现(JDK1.0),采用**数组+链表**结构存储数据,所有公共方法均用`synchronized`修饰实现线程安全。以下是关键实现原理: --- #### 一、核心数据结构 ```java private transient Entry<?,?>[] table; // 底层数组 private int threshold; // 扩容阈值 = 容量 * 负载因子 private float loadFactor; // 默认0.75 // 链表节点定义 private static class Entry<K,V> { final int hash; final K key; V value; Entry<K,V> next; // 链表指针 } ``` --- #### 二、PUT方法源码分析(线程安全关键) ```java public synchronized V put(K key, V value) { // 1. 禁止null键值 if (value == null) throw new NullPointerException(); // [^2][^4] // 2. 计算索引位置 int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; // 取模定位 // 3. 遍历链表处理冲突 for (Entry<?,?> e = table[index]; e != null; e = e.next) { if (e.hash == hash && e.key.equals(key)) { // 键已存在 V old = (V)e.value; e.value = value; // 更新值 return old; } } // 4. 新增节点(头插法) addEntry(hash, key, value, index); return null; } private void addEntry(int hash, K key, V value, int index) { // 5. 扩容检测(元素数 >= 阈值) if (count >= threshold) rehash(); // [^1] // 6. 创建新节点并插入链表头部 Entry<K,V> e = (Entry<K,V>) table[index]; table[index] = new Entry<>(hash, key, value, e); // next指向原头节点 count++; // 元素计数+1 } ``` --- #### 三、GET方法源码分析 ```java public synchronized V get(Object key) { // 1. 计算哈希值 int hash = key.hashCode(); // 2. 定位索引 int index = (hash & 0x7FFFFFFF) % table.length; // 3. 遍历链表查找 for (Entry<?,?> e = table[index]; e != null; e = e.next) { if (e.hash == hash && e.key.equals(key)) { return (V)e.value; // 命中返回 } } return null; // 未找到 } ``` --- #### 四、REHASH扩容机制 ```java protected void rehash() { int oldCapacity = table.length; // 1. 新容量 = 旧容量 * 2 + 1 int newCapacity = (oldCapacity << 1) + 1; // [^1] // 2. 创建新数组 Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; // 3. 迁移数据(遍历旧表) for (int i = oldCapacity; i-- > 0;) { while (Entry<K,V> old = (Entry<K,V>)table[i]) != null) { table[i] = old.next; // 拆解链表 // 4. 重新计算索引 int index = (old.hash & 0x7FFFFFFF) % newCapacity; old.next = (Entry<K,V>)newMap[index]; // 头插法迁移 newMap[index] = old; } } table = newMap; // 切换新数组 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); } ``` --- #### 五、关键特性总结 1. **线程安全**:所有方法用`synchronized`修饰,但高并发下性能差(锁粒度过大)[^4][^5] 2. **哈希冲突**:数组+链表解决冲突,头插法添加新节点 3. **扩容机制**:元素数 ≥ 容量×0.75时,扩容至`2n+1`并重哈希[^1] 4. **禁止null**:键和值均不可为`null`(与HashMap主要区别)[^2] 5. **哈希计算**:直接使用`key.hashCode()`(无扰动优化) --- #### 六、性能对比 | 特性 | HashTable | HashMap | ConcurrentHashMap | |--------------|-----------------|-----------------|-------------------| | **线程安全** | synchronized锁 | 非安全 | 分段锁/CAS | | **null支持** | 禁止 | 允许 | 禁止 | | **扩容** | 2n+1 | 2的幂次 | 分段扩容 | | **性能** | 低(全局锁) | 高 | 中高 | > 现代Java开发建议用`ConcurrentHashMap`替代HashTable,因其采用分段锁实现更高并发度[^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值