一、Map集合简要概述
简述
在Java中,Map
是一个接口,它代表了键值对(key-value pairs)的集合。每个键都映射到一个值,而且一个键只能映射到一个值。键(key)是唯一的,而值(value)可以重复。Map
接口中提供的方法允许我们检索、更新和删除键值对。
特点
- 键唯一性:每个键在映射表中是唯一的,不能有重复的键。
- 值的重复性:映射表中的值可以重复,即不同的键可以映射到相同的值。
- 动态数据结构:映射表可以根据需要动态地添加、删除和修改键值对。
常用实现类
HashMap
:基于哈希表的 Map 接口实现,它允许空键和空值。HashMap 不保证映射的顺序。TreeMap
:基于红黑树的 Map 接口实现,可以按照键的自然顺序或自定义顺序对键进行排序。LinkedHashMap
:类似于 HashMap,但它维护了一个双向链表,可以按照插入顺序或访问顺序来遍历键值对。Hashtable
:和 HashMap 类似,但它是同步的,不允许空键和空值。
二、Map中几个常用方法
可以看出,Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在 Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项);Map中的键不能重复,但值可以重 复。Map接口中定义了如下常用方法:
void clear()
:清除映射表中的所有键值对。boolean containsKey(Object key)
:判断映射表是否包含指定的键。boolean containsValue(Object value)
:判断映射表是否包含指定的值。Set<Map.Entry<K,V>> entrySet()
:返回映射表中所有键值对的 Set 视图。V get(Object key)
:返回指定键所映射的值。boolean isEmpty()
:判断映射表是否为空。Set<K> keySet()
:返回映射表中所有键的 Set 视图。V put(K key, V value)
:将指定的值与此映射中的指定键关联(可选操作)。void putAll(Map<? extends K,? extends V> m)
:将指定映射表中的所有映射复制到此映射表中(可选操作)。V remove(Object key)
:如果存在一个键的映射关系,则将其从映射表中移除并返回该键的值。int size()
:返回映射表中键值对的数量。Collection<V> values()
:返回映射表中所有值的 Collection 视图。
几个案例代码:
HashMap hashMap = new HashMap();//hashMap的创建,hashMap的线程是不安全的
hashMap.put("cn", "8");//添加元素
hashMap.put(null, null);//键和值可以为空
hashMap.put("12", "23");
hashMap.put("2", "3");
System.out.println(hashMap.get("cn"));//通过键获取值
System.out.println(hashMap.get(null));
System.out.println(hashMap.containsKey("cn"));//通过键获取值
System.out.println(hashMap.containsValue("8"));//通过值获取键
hashMap.remove("cn");//删除元素
hashMap.replace(null, "新的值");//修改
三、遍历Map的四种方式
1,使用Entry对象遍历
Map.Entry<K,V>,在Map接口中有一个内部接口Entry(内部类)
作用:当集合一创建,就会在Map集合中创建一个Entry对象,用来记录键与值(键值对对象,键值的映射关系)。
有了Entry对象就可以使用Map中的entrySet方法,把Map集合中的多个Entry对象存入一个Set集合来遍历Set集合,获取Set集合中每一个Entry对象,然后可以使用Entry中的两个方法getKey和getValue来分别获取键和值。
Set<Map.Entry<String, String>> entry = hashMap.entrySet();//遍历方式1
for (Map.Entry<String, String> entrys : entry) {//entry由键和值构成的对象
System.out.println("key:" + entrys.getKey() + "\tvalue:" + entrys.getValue());
}
2,通过键找值的方法
使用了setKey方法,将Map集合中的key值,存储到Set集合,用迭代器或foreach循环遍历Set集合来获取Map集合的每一个key,并使用get(key)方法来获取value值。
Set<String> keys = hashMap.keySet();//遍历方式3,获取所有的键
for (String key : keys) {
System.out.println(hashMap.get(key));//通过获取键来打印值
}
3,.通过values()方法获取所有的值
1.通过values()方法,获取map集合中所有的value的集合.返回值类型为Collection(该接口是 List和Set的父接口,该类型下有Iiterator迭代器,可以用来迭代)。
2.通过Iterator迭代器,迭代出所有的value。
Collection values = map.values();
Iterator iterator = values.iterator();
while(iterator.hasNext()){
Object value = iterator.next();
}
4,通过forEach方法遍历
在Java中,forEach
是一个常用的方法,用于遍历集合(如List
、Set
、Map
等)中的元素。它是在Java 8中引入的,作为lambda表达式和Stream API的一部分,使得集合的遍历变得更加简洁和易读。
//遍历方式4
hashMap.forEach((key, values) -> {
System.out.println("key:" + key + "\tvalue" + values);
});
四、常用实现类
HashMap
基于哈希表的 Map 接口实现,它允许空键和空值。HashMap 不保证映射的顺序。
请看Map使用代码示例:
import java.util.*;
public class HashMapDemo {
public static void main(String[] args) {
//1.创建HashMap对象,用来存放国家英文简写与国家中文全称
Map map = new HashMap();
//2.往HashMap添加元素(键-值对)
map.put("CN", "中华人民共和国");
map.put("RU", "俄罗斯联邦");
map.put("US", "美利坚合众国");
map.put("FR", "法兰西共和国");
//3.获取key为"CN"对应的值
String value = (String) map.get("CN");
System.out.println(value);
//4.向上集合元素的个数
System.out.println("map集合共有:"+map.size()+"个元素");
//5.判断是否存在"FR"的键
System.out.println("map集合中是否包含FR吗?"+map.containsKey("FR"));
//6 获取Map中键的集合和值得集合
System.out.println("map中key的集合:"+map.keySet());
System.out.println("map中value的集合:"+map.values());
}
}
HashMap的原理
HashMap集合中,存储数据,实际上是一个Node类型的数组(JDK1.7是Entry类型),中间的每个元素 又是一个链表,也就是我们所说的哈希表。
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个 链表中,但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低.而 JDK1.8中,哈希表存储采用数组+链表+红黑树实现.当链表长度超过阈值(8),先判断table的长度是否大于 64,就转换为红黑树,这样大大减少了查询时间.如果小于64,就通过扩容的方式来解决,避免红黑树结构 化。
简单的来说,哈希表是由数组+链表+红黑树实现的
TreeMap
TreeMap
是 Java 中 Map
接口的一个实现,它基于红黑树(Red-Black tree)实现。红黑树是一种自平衡的二叉搜索树,能够保证树的平衡,从而使得 TreeMap
的操作(如 get
, put
, remove
等)都能在对数时间复杂度内完成,即 O(log n)
。
示例代码
package Java基础.常用类.集合.map;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
public class TreemapTest {
public static void main(String[] args) {
TreeMap<String, Integer> map = new TreeMap<>();
map.put("b", 2);
map.put("a", 1);
map.put("c", 3);
map.put("d", 4);
map.put("g", 7);
//默认已经按自然排序
map.forEach((key,value)->{
System.out.println(key+"--->"+value);
});
//获取大于或等于key的键值对
Map.Entry entry = map.ceilingEntry("f");
System.out.println(entry.getKey() + "---" + entry.getValue());
//获取大于或等于key的键
System.out.println(map.ceilingKey("e"));
//严格大于key的实体
entry = map.higherEntry("d");
System.out.println(entry.getKey() + "---" + entry.getValue());
//获取第一个实体
entry = map.firstEntry();
System.out.println(entry.getKey() + "---" + entry.getValue());
//获取第一个key
String key = map.firstKey();
System.out.println(key);
//获取最后一个
entry = map.lastEntry();
//获取小于或等于key的实体
entry = map.floorEntry("c");
System.out.println(entry.getKey() + "---" + entry.getValue());
//获取小于或等于key的键
System.out.println(map.floorKey("c"));
//从起点截取到终点之间不包括终点的一段实体
SortedMap sm = map.subMap("b", "d");
Set<Map.Entry> entries = sm.entrySet();
for (Map.Entry ev : entries) {
System.out.println("key:" + ev.getKey() + "\tvalue:" + ev.getValue());
}
}
}
总结
Java中的Map
接口代表了一个键值对集合,其中每个键都映射到一个值。Map
的常用实现类包括HashMap
、TreeMap
、LinkedHashMap
每个都有其独特的特性和用途。
-
HashMap
是基于哈希表实现的,提供快速的查找、插入和删除操作。它不保证任何顺序,允许使用null
键和null
值,但不是线程安全的。 -
TreeMap
基于红黑树实现,保证了键的顺序。它可以根据键的自然顺序或者创建时提供的Comparator
进行排序,不允许null
键,但允许null
值,不是线程安全的。 -
LinkedHashMap
继承自HashMap
,在HashMap
的基础上增加了链表结构,用于维护插入顺序。它可以选择性地维护访问顺序,不是线程安全的。 -
forEach
是一个常用的方法,用于遍历集合中的元素。在Java 8中引入,使得集合的遍历变得更加简洁和易读。forEach
方法接受一个Consumer
接口的实现,用于对集合中的每个元素执行操作。