当前位置: 首页 > article >正文

字节面试Java基础部分——HashMap

字节面试Java基础部分

面试管:Java应该很熟悉吧,接下来问你几个Java基础问题:

HashMap 是什么样的数据结构

JDK 7 中,HashMap 由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。

在 JDK 8 中,HashMap 由“数组+链表+红黑树”组成。

HashMap 是干什么用的

HashMap 在 Java 中主要用于以下几个方面:

键值对存储:HashMap 允许将数据以键值对的形式存储。这使得通过键快速查找、插入和删除相关值变得非常高效。

快速查找:由于其基于哈希表的实现,HashMap 可以在平均常数时间内完成插入、删除和查找操作,适用于需要频繁访问数据的场景。

实现字典功能:HashMap 可以用作字典,存储相关信息,例如将单词作为键,定义或翻译作为值。

缓存机制:可以用 HashMap 来实现简单的缓存,存储计算结果以提高性能,避免重复计算。

数据分组:可以将相同类别的数据分组,例如以学生的 ID 为键,以学生的成绩为值,快速访问某个学生的成绩。

java HashMap 对于存入的类有什么要求吗 譬如我存入一个Object 对于我存入的Object 有什么要求吗

在 Java 中,当你使用 HashMap 存储对象(无论是自定义类还是其他类型的对象)时,有几个重要的要求和注意事项:

1. hashCode() 方法

  • 存入 HashMap 的对象(作为键)必须正确实现 hashCode() 方法。哈希表的存储和查找依赖于哈希值。对象的哈希值应该尽可能均匀分布,以减少冲突。

  • 默认情况下,Object 类的 hashCode() 方法是基于对象的内存地址生成的。若你想使用自定义类作为键,需要重写 hashCode() 方法。

2. equals() 方法

  • 你还需要重写 equals() 方法,以确保当两个对象被认为是相等时,它们的哈希值也相等。HashMap 会根据键的相等性来进行查找和删除操作。

3. 不可变性

  • 虽然 HashMap 允许存储可变对象,但如果你将可变对象作为键,一旦这些对象的状态发生改变,可能会导致 HashMap 中的查找和删除操作失败。因此,最好将不可变对象(如字符串、包装类等)或在使用后不再修改的对象作为键。

示例代码

下面是一个示例,展示了如何为自定义类实现 hashCode()equals() 方法,以便可以用作 HashMap 的键:

import java.util.HashMap;
import java.util.Objects;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

public class HashMapExample {
    public static void main(String[] args) {
        HashMap<Person, String> map = new HashMap<>();
        Person p1 = new Person("Alice", 30);
        Person p2 = new Person("Bob", 25);
        
        map.put(p1, "Engineer");
        map.put(p2, "Designer");

        System.out.println(map.get(new Person("Alice", 30))); // 输出 "Engineer"
    }
}

总结

  • 重写 hashCode()equals() 方法:确保 HashMap 正确处理键的相等性。
  • 考虑不可变性:尽量使用不可变对象作为键,以避免意外的问题。

java HashMap 中的Key是用户自定义类A的对象a,Value假设为123 现在用户要取出123 一定需要拿到对象a吗

HashMap 中,如果键是用户自定义类 A 的对象 a,那么要通过键取出相应的值(例如 123),你必须提供一个与 a 相等的键对象。也就是说,不能直接用 123 来获取对应的值,而是需要用一个与 a 相同的 A 类的对象。

具体原因

  1. 哈希存储机制HashMap 使用键的哈希值来确定值的位置。当你调用 get() 方法时,需要提供一个键,这个键的哈希值将用于查找。

  2. equals() 方法:为了能够找到相应的值,提供的键对象必须与原来存入的键对象在逻辑上相等。也就是说,equals() 方法必须返回 true

示例

假设你有以下的自定义类 A

import java.util.HashMap;
import java.util.Objects;

class A {
    private String name;

    public A(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        A a = (A) obj;
        return Objects.equals(name, a.name);
    }
}

public class HashMapExample {
    public static void main(String[] args) {
        HashMap<A, Integer> map = new HashMap<>();
        A keyA = new A("example");
        map.put(keyA, 123);

        // 要取出 123,必须提供与 keyA 相同的对象
        A searchKey = new A("example");
        Integer value = map.get(searchKey); // 将返回 123

        System.out.println(value); // 输出 123
    }
}

总结

  • 需要相等的对象:要从 HashMap 中取出值,必须使用与存入时相同或逻辑上等价的键对象。
  • 提供相同状态的对象:即使是不同的对象实例,只要它们的状态(字段值)相同,且正确重写了 hashCode()equals() 方法,就可以取出对应的值。

HashMap 是线程安全的吗

不是

你是如何定义线程安全的

线程安全是程序设计中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的公用变量,使程序功能正确完成。

HashMap为什么不是线程安全的

HashMap 不是线程安全的,主要原因如下:

1. 数据结构设计

  • HashMap 是基于哈希表实现的,主要使用数组和链表(或红黑树)来存储数据。当多个线程同时对同一个 HashMap 进行读写操作时,可能会发生数据竞争,导致不一致性。

2. 状态变化

  • HashMap 中,插入、删除和更新操作可能会改变其内部状态。例如,当一个元素被插入时,可能需要重新计算哈希值并调整数组的大小或结构(如链表或树)。如果另一个线程在此期间也对 HashMap 进行操作,就可能导致数据丢失或数据结构损坏。

3. 非原子性操作

  • HashMap 的许多操作(如 putremove)并不是原子的,可能由多个步骤组成。在多线程环境中,如果一个线程正在执行这些操作,另一个线程可能会同时修改数据,导致结果不可预测。

4. 并发问题

  • 当多个线程同时对 HashMap 进行读和写操作时,可能会出现并发问题,例如:
    • 数据丢失:一个线程可能覆盖另一个线程刚插入的值。
    • 抛出异常:例如,在扩容时可能会抛出 ConcurrentModificationException
    • 读取不一致:一个线程读取数据时,另一个线程可能在修改数据,导致读取到的内容不准确。

解决方案

如果需要在多线程环境中使用 HashMap,可以考虑以下几种替代方案:

  1. 使用 Collections.synchronizedMap
    HashMap 包装为同步的 Map。

    Map<K, V> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
    
  2. 使用 ConcurrentHashMap
    ConcurrentHashMap 是 Java 提供的线程安全的哈希表实现,支持高并发访问。

    ConcurrentHashMap<K, V> concurrentMap = new ConcurrentHashMap<>();
    
  3. 使用显式锁
    使用 ReentrantLock 等锁机制手动控制对 HashMap 的访问。

总结来说,HashMap 不是线程安全的,因为它的设计没有考虑并发访问的情况。对于需要线程安全的场景,应该使用其他线程安全的集合类。

参考文献

相关回答参考ChatGPT

原创不易
转载请标明出处
如果对你有所帮助 别忘啦点赞支持哈
请添加图片描述


http://www.kler.cn/a/381131.html

相关文章:

  • conda 环境报错error while loading shared libraries: libpython3.9.so.1.0
  • uniapp跨平台开发---webview调用app方法
  • UG NX二次开发(C#)-机电概念设计-UIStyler中selection块选择信号等对象的过滤器设置
  • springboot根据租户id动态指定数据源
  • Linux(Ubuntu)命令大全——已分类整理,学习、查看更加方便直观!(2024年最新编制)
  • vsCode怎么使用vue指令快捷生成代码
  • QT 如何使QLabel的文字垂直显示
  • 弹性裸金属服务器和传统裸金属服务器有什么区别?
  • 自制inscode项目推荐:色块小游戏
  • 截图工具 for Linux --- 你用过吗?
  • operator[ ]和迭代器,auto,for流,reserve
  • 【测试小白--如何写好测试用例--测试用例编写的方法+结合常见登录模块为实例--保姆级教学】
  • vue通过iframe方式嵌套grafana图表
  • ENSP (虚拟路由冗余协议)VRRP配置
  • 基于Matlab的语音识别
  • 仿真APP助力汽车零部件厂商打造核心竞争力
  • MySQL表的增删改查(CRUD3约束)
  • HTTP请求和请求体Body
  • 【Oracle】空格单字符通配符查询匹配失败
  • 【PMP】学习总结
  • FreeMarker模版引擎入门及实战
  • 人工智能学习--归一化(Normalization)
  • 编译工具与文件学习(一)-YAML、repos、vcstoolcolcon
  • 【大模型LLM面试合集】大语言模型架构_chatglm系列模型
  • STM32移植RT-Thread---时钟管理
  • 【MyBatis源码】CacheKey缓存键的原理分析