当前位置: 首页 > 图灵资讯 > 技术篇> 问题驱动-Map数据结构

问题驱动-Map数据结构

来源:图灵教育
时间:2023-07-02 17:11:19

1、 引言

Map是Java中常用的数据结构,它提供了一种按键快速访问值的存储方法。在本文中,我将学习Java中的Map数据结构

从至少以下几个方面来说明什么是map。、使用Map有什么好处,Map的底层原理,Map中的key和value是什么,为什么Map中的key值不能重复,Map中的key值和Hash有什么关系。

以及Hashmap、了解Treemap和LinkedHashmap三个常用的Map实现类别。我将逐步分析它们的初始化、添加和获取元素、遍历和删除元素。

2、什么是Map?

Map是Java中的一个接口,它代表了键值对的映射关系。它允许我们通过Key访问Value。在Map中,每个Key都是唯一的,与Key对应的Value是一对一的。

使用Map的好处

使用Map有很多好处,其中有几个重要的好处:

  1. 快速访问:通过给定的Key,可以快速访问相应的Value,无需遍历整个集合。
  2. 灵活性:Map不仅可以存储基本数据类型的值,还可以存储自定义对象作为Value,使其非常灵活。
  3. 动态增长:Map的大小可根据需要动态增长,无需事先指定容量。
  4. 数据分类:Map可用于数据分类、分组和存储,便于后续检索和处理。
Map的底层原理

在Java中,常用的Map实现类有HashMap、TreeMap和LinkedHashMap。这些实现类在底层的数据组织模式和搜索算法上略有不同。

Hashmap使用散列表(Hash Table)作为底层数据结构,它通过将Key的Hash值映射到数组索引上,并采用链地址法解决Hash冲突。

使用红黑树使用TreeMap(Red-Black Tree)作为一种基础数据结构,它可以对Key进行排序,并提供有序的遍历。

LinkedHashmap继承自Hashmap,它通过维护Hashmap的双向链表来保证插入顺序或访问顺序。

根据实际情况选择不同的Map实现类,可根据需要平衡时间复杂性和空间复杂性。

以下将了解这些实现类的一些方法。

Key和Value的含义

在Map中,Key用于识别唯一的键值对,相当于数据索引。Value是与Key相关的数据。对于同一个Key,只能有一个对应的Value,但不同的Key可以对应不同的Value。

例如,我们可以创建一个Map,以每个人的名字为Key,以他们的年龄为Value。通过Key,我们可以快速找到相应的年龄。

为什么Key值不能重复?

Map要求每个Key都是唯一的,因为Map需要通过Key定位和访问Value。如果有多个相同的Key,Map无法确定应该返回哪个Value。

当我们用put向Map添加键值时,如果Key已经存在,新的Value将覆盖旧的Value。因此,Key的独特性保证了在Map中定位Value的准确性。

Key值与Hash的关系

基础数组中Key的位置是通过计算Key的HashMap和LinkedHashMap在Java中的HashMap来确定的。

在Hashmap中,当我们向其中插入一个键值时,Hashmap将首先通过Key的hashcode()计算Key的哈希值。然后,Hashmap将根据哈希值对数组的长度进行模拟,以获得Key在底层数组中的索引位置。如果多个Key的哈希值映射到同一个索引位置,Hashmap将使用链表或红黑树来处理冲突。

在LinkedHashmap中,它通过维护Hashmap的双向链表来保证插入顺序或访问顺序。Hashmap中的Key与链表节点相互关联,实现了按插入顺序或访问顺序迭代Map的键值对。

由此可见,Hash在Map中起着定位Key的作用。通过计算Key的哈希值,可以快速定位Key在底层数组中的位置,从而提高搜索效率。同时,Hash值的独特性也保证了Key在Map中的独特性。

由于Hash值是通过哈希函数计算出来的,因此存在一定的碰撞概率。因此,在使用自定义对象作为Key时,我们需要重写hashcode()方法,以确保生成的hash值能够准确地表示对象的唯一性,并重写equals()方法来处理碰撞冲突的逻辑。这样,即使Hash值相同,也能保证不同的Key对象能够正确比较和搜索。

3. Hashmap3.1 Hashmapp的初始化

在Java中,我们可以使用HashMap类来创建HashMap对象。以下是一些常见的初始化方法:

  • 默认构造函数的使用:HashMap<String, Integer> map = new HashMap<>();
  • 指定初始容量:HashMap<String, Integer> map = new HashMap<>(16);
  • 指定初始容量和加载因子:HashMap<String, Integer> map = new HashMap<>(16, 0.75f);
3.2 添加和获取元素

在Hashmap中添加元素时,需要使用put()法,并提供键和值。以下是一个例子:

HashMap<String, Integer> map = new HashMap<>();map.put("apple", 5);map.put("banana", 10);map.put("orange", 8);

get()方法可用于获取HashMap中的元素,并提供键。以下是一个例子:

int appleCount = map.get("apple");System.out.println(“苹果数量:” + appleCount);

3.3 Hashmap遍历

Hashmap可以使用多种方法,如迭代器,for-或使用Java 8Lambda表达式。以下是使用迭代器遍历Hashmap的示例:

Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();while (iterator.hasNext()) {    Map.Entry<String, Integer> entry = iterator.next();    System.out.println“水果:” + entry.getKey() + 数量:",数量:" + entry.getValue());}

3.4 删除元素

可以使用remove()法从Hashmap中删除元素,并提供键。以下是一个例子:

map.remove("banana");

3.5实现原理

①Hashmapput()方法,其实现原理如下:

首先,Hashmap通过哈希函数处理要存储的键值,获得哈希码(hash code)。

接下来,Hashmap将根据哈希码在内部数组中找到该键对应的索引位置。

如果该位置没有其他键值对存在,则可以直接将新键值对存储在该位置。

如果该位置有其他键对,Hashmap将使用链表或红黑树来处理冲突。它将在链表(或树)中比较存储的键的哈希码和键值是否等于存储的键值。

如果找到相等键,HashMap将替换键对应的值。

如果找不到相等的键,Hashmap会在链表(或树)的末尾添加新的键值。

②当我们调用Hashmap的get()方法时,其实现原理相似:

第一,HashMap通过哈希函数计算键的哈希码。

接下来,Hashmap将根据哈希码在内部数组中找到该键对应的索引位置。

如果该位置没有键值对,则表示该键不存在于Hashmap中,返回null。

如果这个位置有键值对,Hashmap将遍历链表(或树),比较存储的键的哈希码和键值是否等于要获得的键值。

如果找到相等键,HashMap返回键对应的值。

如果链表(或树)没有找到相等的键,则表示键不存在于Hashmap中,返回null。

这样,Hashmap就能有效地实现put()和get()方法,快速存储和搜索键值对,提供快速的数据访问能力。

4. TreeMap4.1 TreeMapp的初始化

与Hashmap类似,我们可以使用Treemap类来创建Treemap对象。以下是一些常见的初始化方法:

  • 默认构造函数的使用:TreeMap<String, Integer> map = new TreeMap<>();
  • 使用Comparator初始化:TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
4.2 添加和获取元素

添加和获取元素的方法类似于HashMap,使用put()方法添加元素,使用get()方法获取元素。

map.put("apple", 1);map.put("banana", 2);map.put("orange", 3);int value = map.get("apple");System.out.println(value);  // 输出:1

4.3 TreeMap遍历

与HashMap类似,遍历TreeMap的方法可以使用迭代器,for-each循环或Lambda表达式。

以下是Treemap使用for-each循环的例子:

for (Map.Entry<String, Integer> entry : map.entrySet()) {    String key = entry.getKey();    int value = entry.getValue();    System.out.println(key + " : " + value);}

4.4 删除元素

从Treemap中删除元素的方法类似于Hashmap,使用remove()方法。

map.remove("banana");

5. LinkedHashmap5.1 LinkedHashmap的初始化

LinkedHashmap是一个有序的Map实现类,保留了元素的插入顺序。初始化方法类似于Hashmap。

默认构造函数的使用:

LinkedHashMap<String, Integer> map = new LinkedHashMap<>();

5.2 添加和获取元素

添加和获取元素的方法与Hashmap相似,使用put()法添加元素,使用get()法获取元素。

map.put("apple", 1);map.put("banana", 2);map.put("orange", 3);int value = map.get("apple");System.out.println(value);  // 输出:1

5.3 LinkedHashMapp

LinkedHashmap遍历的方法与Hashmap相似,可使用迭代器,for-each循环或Lambda表达式。

for (Map.Entry<String, Integer> entry : map.entrySet()) {    String key = entry.getKey();    int value = entry.getValue();    System.out.println(key + " : " + value);}

5.4 删除元素

从LinkedHashmap中删除元素的方法与Hashmap相似,采用remove()方法。

6、总结

我们可以看到Hashmapp、Treemap和LinkedHashmap都是非常有用和灵活的Map实现类,每一个都适用于不同的使用场景。当我们需要无序高效的Map时,我们可以选择HashMap;当我们需要有序的Map时,我们可以选择TreeMap;当我们需要一个保留插入顺序并支持特殊操作的Map时,我们可以选择LinkedHashMap。

因此,当我们需要使用Map数据结构时,我们可以根据具体需要选择合适的数据结构来解决问题。