Map 接口实现类的特点
Map
与Collection
并列存在。用于保存具有映射关系的数据:Key-Value
Map
中的key
和value
可以是任何引用类型的数据,会封装到HashMap$Node
对象中Map
中的key
不允许重复,原因和HashSet
一样,前面分析过源码.Map
中的value
可以重复Map
的key
可以为null
,value
也可以为null
,注意key
为null
,只能有一个,value
为null
,可以多个.- 常用
String
类作为Map
的key
key
和value
之间存在单向一对一关系,即通过指定的key
总能找到对应的value
Map
存放数据的key-value
示意图,一对k-v
是放在一个Node
中的,有因为Node
实现了Entry
接口,有些书上也说一对k-v
就是一个Entry
package collection_;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Gin
* @Description:
* @Modified By: Gin
* @Date: Created in 9:04 2021/9/23
*/
public class Map_ {
public static void main(String[] args) {
// 1. Map 与 Collection 并列存在。用于保存具有映射关系的数据:Key-Value
// 2. Map 中的 key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中
// 3. Map 中的 key 不允许重复,原因和 HashSet 一样,前面分析过源码.
// 4. Map 中的 value 可以重复
// 5. Map 的 key 可以为 null ,value 也可以为 null,注意 key 为 null ,只能有一个, value 为 null ,可以多个.
// 6. 常用 String 类作为 Map 的 key
// 7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value
Map map = new HashMap();
map.put("no1", "Gin");
map.put("no2", "Sherry");
map.put("no2", "Vodka");
map.put("no3", "Gin");
map.put(null, null);
map.put(null, "Rum");
map.put("no4", null);
map.put("no5", null);
map.put(1, "Vermouth");
map.put(new Object(), "Bourbon");
System.out.println(map);
// 通过 get 方法传入 key,会返回对应的 value
System.out.println(map.get(1));
}
}
Map 特点 8 :源码解读
Map
存放数据的key-value
示意图,一对k-v
是放在一个Node
中的,有因为Node
实现了Entry
接口,有些书上也说一对k-v
就是一个Entry
package collection_;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @Author: Gin
* @Description:
* @Modified By: Gin
* @Date: Created in 15:48 2021/9/23
*/
public class MapSource_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1", "Gin"); // k-v
map.put("no2", "Sherry"); // k-v
/*
tab[i] = newNode(hash, key, value, null);
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
解读:
1. k-v 最后是 HashMap$Node node = newNode(hash, key, value, null);
2. k-v 为了方便程序员的遍历,还会创建 EntrySet 集合,该集合存放的元素的类型为 Entry,
而一个 Entry 对象就有 k-v,EntrySet<Entry<k,v>>,即:transient Set<Map.Entry<K,V>> entrySet;
3. Entry 对象的 K, V 会指向 newNode 中的 key, value,即:数据并不会存放到 Entry 对象中
4. 在 entrySet 中,定义的类型是 Map.Entry,但是实际存放的还是 HashMap$Node
5. 这是因为 HashMap$Node 实现了 Map.Entry 接口: static class Node<K,V> implements Map.Entry<K,V>
6. 当把 HashMap$Node 对象存放到 entrySet 就会方便程序员遍历,因为 Map.Entry 提供了两个重要的方法:
K getKey()、V getValue()
*/
Set set = map.entrySet();
System.out.println(set.getClass()); // set 的运行类型:HashMap$EntrySet,set 中存放的就是一个一个的 Entry 对象
for (Object obj : set) {
// System.out.println(obj.getClass()); // 每一个 Entry 对象的运行类型:HashMap$Node
// 通过 Map.Entry 的 getKey、getValue 方法获取 k-v
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey() + " - " + entry.getValue());
// no2 - Sherry
// no1 - Gin
}
// 7. 还提供了 keySet() 和 values() 方法
// 分别用于获取 map 中所有的 key 和 value
Set set1 = map.keySet();
System.out.println(set1.getClass()); // HashMap$KeySet
Collection values = map.values();
System.out.println(values.getClass()); // HashMap$Values
}
}
Map 接口常用方法
- put:添加
- remove:根据键删除映射关系
- get:根据键获取值
- size:获取元素个数
- isEmpty:判断个数是否为0
- clear:清除
- containsKey:查找键是否存在
package collection_;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: Gin
* @Description:
* @Modified By: Gin
* @Date: Created in 16:56 2021/9/23
*/
public class MapSource {
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超", new Bookkk("",100));
map.put("邓超", "孙丽");
map.put("王宝强", "马蓉");
map.put("宋喆", "马蓉");
map.put("刘令博", null);
map.put(null, "刘亦菲");
map.put("鹿晗", "关晓彤");
Object object = map.get("邓超");
System.out.println(object);
map.remove("鹿晗");
System.out.println(map);
boolean containsKey = map.containsKey(null);
System.out.println(containsKey);
System.out.println(map.containsValue("刘亦菲"));
System.out.println(map.size());
System.out.println(map.isEmpty());
map.clear();
/*
输出结果:
孙丽
{邓超=孙丽, 宋喆=马蓉, 刘令博=null, null=刘亦菲, 王宝强=马蓉}
true
true
5
false
*/
}
}
class Bookkk{
private String name;
private int price;
public Bookkk() {
}
public Bookkk(String name, int price) {
this.name = name;
this.price = price;
}
}
Map 的遍历
package collection_;
import java.util.*;
/**
* @Author: Gin
* @Description:
* @Modified By: Gin
* @Date: Created in 17:04 2021/9/23
*/
public class MapFor {
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超", "孙丽");
map.put("王宝强", "马蓉");
map.put("宋喆", "马蓉");
map.put("刘令博", null);
map.put(null, "刘亦菲");
map.put("鹿晗", "关晓彤");
// 第一组:先取出所有的 key,再根据所有的 key 取出对应的 value
Set keySet = map.keySet();
// 1. 增强 for
System.out.println("==== 增强 for ====");
for (Object key : keySet) {
System.out.println(key + " - " + map.get(key));
}
// 2. 迭代器
System.out.println("==== 迭代器 ====");
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + " - " + map.get(key));
}
// 第二组:去除所有的 values
Collection values = map.values();
// 这里可以使用所有的 collections 使用的遍历方法
// 1. 增强 for
System.out.println("==== 增强 for ====");
for (Object value : values) {
System.out.println(value);
}
// 2. 迭代器
System.out.println("==== 迭代器 ====");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object value = iterator1.next();
System.out.println(value);
}
// 第三组:通过 entrySet 来获取 k-v
// 1. 增强 for
System.out.println("==== 第三种 增强 for k-v 方法 ====");
Set entrySet = map.entrySet();
for (Object entry : entrySet) {
// 将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry)entry;
System.out.println(m.getKey() + " - " + m.getValue());
}
// 2. 迭代器
System.out.println("==== 第三种 迭代器 k-v 方法 ====");
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object entry = iterator2.next();
// System.out.println(entry.getClass()); // HashMap$Node
// 向下转型:因为 Node 没有相应的方法,所以转型为 Entry
Map.Entry m = (Map.Entry)entry;
System.out.println(m.getKey() + " - " + m.getValue());
}
}
}
练习
使用 HashMap
添加 3 个员工对象,要求
键:员工id
值:员工对象
并遍历显示工资 > 28000 的员工(遍历方式最少两种)
员工类:姓名、工资、员工id
package collection_;
import java.util.*;
/**
* @Author: Gin
* @Description:
* @Modified By: Gin
* @Date: Created in 18:50 2021/9/23
*/
public class MapExercise {
public static void main(String[] args) {
Map map = new HashMap();
map.put(1, new Emp("Gin", 35600, 1));
map.put(2, new Emp("Vermouth", 40000, 2));
map.put(3, new Emp("Vodka", 28000, 3));
Set keySet = map.keySet();
// 取出所有的 key ,增强 for
System.out.println("===第一种遍历方式===");
for (Object key : keySet) {
Emp emp = (Emp) map.get(key);
if(emp.getSal() > 28000){
System.out.println(emp);
}
}
// 取出所有的 key ,迭代器
System.out.println("===第二种遍历方式===");
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
Emp emp = (Emp) map.get(key);
if(emp.getSal() > 28000){
System.out.println(emp);
}
}
// 使用 entrySet 迭代器遍历
System.out.println("===第三种遍历方式===");
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Map.Entry entry = (Map.Entry) iterator1.next();
Emp emp = (Emp) entry.getValue();
if(emp.getSal() > 28000){
System.out.println(emp);
}
}
}
}
// 员工类:姓名、工资、员工id
class Emp{
private String name;
private double sal;
private int id;
public Emp() {
}
public Emp(String name, double sal, int id) {
this.name = name;
this.sal = sal;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Emp emp = (Emp) o;
return Double.compare(emp.sal, sal) == 0 && id == emp.id && Objects.equals(name, emp.name);
}
@Override
public int hashCode() {
return Objects.hash(name, sal, id);
}
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", sal=" + sal +
", id=" + id +
'}';
}
}
HashMap 小结
Map
接口的常用实现类:HashMap
、Hashtable
和Properties
。HashMap
是Map
接口使用频率最高的实现类。HashMap
是以key-val
对的方式来存储数据(HashMap$Node类型)key
不能重复,但是值可以重复,允许使用null
键和null
值。- 如果添加相同的
key
,则会覆盖原来的key-val
,等同于修改(key不会替换,val会替换) - 与
HashSet
一样,不保证映射的顺序,因为底层是以hash
表的方式来存储的(jdk8HashMap底层:数组 + 链表 + 红黑树) HashMap
没有实现同步,因此是线程不安全的,方法没有做同步互斥操作,没有synchronized