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有很多好处,其中有几个重要的好处:
- 快速访问:通过给定的Key,可以快速访问相应的Value,无需遍历整个集合。
- 灵活性:Map不仅可以存储基本数据类型的值,还可以存储自定义对象作为Value,使其非常灵活。
- 动态增长: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);
在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());
添加和获取元素的方法类似于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 LinkedHashMappLinkedHashmap遍历的方法与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数据结构时,我们可以根据具体需要选择合适的数据结构来解决问题。