【Java寻宝之旅】哈希表介绍

前言

哈希表是一种基于哈希算法的数据结构,它供了快速的数据访问能力。哈希表通常通过使用哈希函数将键(key)映射到表中的位置来访问数据,这使得哈希表在查找、插入和删除操作具有很高的效率。

概念

哈希表是一种通过使用哈希函数来计算数据存储位置的数据结构。它允许用户通过键值对(key-valuepairs)来存储和检索数据。键(key)是唯一的,用于通过哈希函数计算数据的存储位置;值(value)是与键相关联的数据

 

哈希函数

哈希函数是哈希表的核心,它负责将键映射到哈希表中的一个位置。一个优秀的哈希函数应该满足以下条件:

  1. 确定性:相同键总是映射到相同的位置。
  2. 高效性:计算速度快。
  3. 均匀性:键应该均匀分布到哈希表的各个位置,避免聚集。

哈希函数的设计

设计一个好的哈希函数通常需要考虑以下几个方面:

  • 位操作:使用位运算符(如异或、与、或、非、左移、右移)可以提高哈希函数的效率。
  • 乘性变换:乘以一个小于哈希表大小的数,可以提哈值的分布均匀性。
  • 合操作:将不同的哈希值混合起来,可以进一步降低冲突的概率。

 Java中的哈希函数的实现:

  public int hashCode() {
        int hash = 7;
        hash = 31 * hash + Integer.hashCode(this.hashCode()); // 假设this.hashCode()返回一个in值
        return hash;
    }

在实际的Java实现中,hashCode()方法通常会根据对象的实际内容来计算哈希值 

自定义哈希函数

在某些情况下,你可能需要为自定义对象实现哈希函数。

 

public class Person {
    private String name;
    private int age;

    public PersonString name, int age) {
        ths.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Persn person = (Person) obj;
        return age == persn.ag && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        int result = 7; // 一个常用的哈希种子值
       esult = 31 * result + (name == null ? 0 : name.hashCode());
        result = 31 * result + age;
        return result;    }
}

在这个代码中,hashCode()方法使用了31这个质数作为乘数,并且对字符串使用了String类的hashCode()方法来计算哈希值。 

结论

哈希函数是哈希表中不可或缺的一部分,它直接影响到哈希表的性能设计哈希函数时,需要考虑确定性、高效性均匀性和雪崩效应。在Java中,通过重写Object类的hashCode()方法,可以为自定义对象实现哈希函。一个好的哈希函数对于提高哈希表的性能至关要。

HashMap的使用

HashMap 是 Java 中最常用的集合之一,它基于哈希表的 Map 接口实现,允许存储键值对,并提供快速的查找能力。以下是 HashMap 的一些基本用法和特性:

基本特性

  • 键值对存储HashMap 存储的数据结构是键值对,即每个键映射到一个值。
  • 键的唯一性HashMap 中的键是唯一的,如果插入一个已经存在的键,则会用新的值替换旧的值。
  • 无序HahMap 不保证元素的顺序,如果需要有序的键值对,可以使用 LinkedHashMap
  • 非同步HashMap 不是线程安全的。如果需要线程安全的 HashMap,可以使用 ConcurrentHashMap 或将 HashMap 包装 Collections.synchronizedMap() 方法中。

代码示例 

import java.util.HashMap;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个 HashMap 实例
        HashMap<String, Integer> map = new HashMap<>();
    }
}

添加元素 

// 向 HashMap 中添加键值对
map.put("key1", 100);
map.put("key2", 200);

访问元素

// 通过键获取值
Integer value = map.get("key1");
System.out.println("The value for 'key1' is: " + vaue);```

#### 检查键是否存在

```java
// 检查键是否存在
boolean containsKey = map.ontainsKey("key1");
System.out.println("Does 'key1' exist? " + containsKey);

 删除元素

// 删除键值对
map.remove("key1");

获取 HashMap 的大小 

// 获取 HashMap 中键值对的数量
int size = map.size();
System.out.println("The size of the map is: " + size);

遍历 HashMp 

// 遍历 HashMap
fr (MapEntry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.etValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

线程安全的 HashMap

 如果你的应用程序是多线程的,并且多个线程可能会同时访问和修改 HashMap,那么你应该使用 ConcurrentHashMap 或者将 HashMap 包装在 Collections.synchronizedMap() 中,以确保线程安全。


public class ThreadSfeHashMapExample {
    public static void main(String[] args) {
        // 使用 Collections.synchronizedMap 包装 HashMap
        Map<String, Integer> threadSafeMap = Collections.synchronizedMap(new HashMap<>());
        
        // 或者直接使用 ConcurrentHashMap
        Map<String, Intege> concurrentHashMap = new ConcurrentHashMap<>();
    }
}

性能考虑

  1. 初始容量HashMap 的初始容量可以影响性能。如果事先知道将要存储的元素数量,可以设置一个初始容量来减少重新哈希的次数。
  2. 负载因子HashMa 的负载因子决定了何时进行扩容。默认的负载因子是 0.75,这意味着当 HashMap 的大小达到其容量的 75% 时,会进行扩容。

总结

HashMap 是一个非常灵活且功能强大的数据结构,适用于需要快速查找的场景。然而,使用时需要注意其线程安全性和性能调优,以确保应用程序的稳定性和效率。

冲突解决

由于哈希函数的值域有限,而键的数量可能非常大,因此不可避免地会出现多个键映射到同一个位置的情,这种现象为冲突(collision)。Java中的哈希表使用以下几种方法来解决冲突:

  1. 链地址法:在每个位置维护一个链表,所有映射到该位置的键都存储在这个链表中。
  2. 开放寻址法:寻找空的位置来存储发生冲突的键。
  3. 双重散列:使用第二个哈希函数来寻找另一个位置。
  4. 再散列:使用不同的哈希函数重计算哈希值。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.祝小码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值