概述
Map<K,V>,K - 由此地图维护的键的类型,V - 映射值的类型
将键映射到值的对象。 地图不能包含重复的键; 每个键可以映射到最多一个值。 该接口代替了Dictionary类,它是一个完全抽象的类而不是接口。
Map接口提供了三个集合视图 ,允许将映射的内容视为一组键,值的集合或键值映射集合。 地图的顺序被定义为其中在地图上的集合视图迭代返回元素的顺序。 一些地图实现,如TreeMap类,对其顺序做出特定的保证; 其他人,像HashMap类,不要。
特点
- 双列集合,一个元素包含两个值(一个时key,一个时value)
- key和value的数据类型可以相同,也可以不同
- key不允许重复,value可以重复
- key和value是一一对应的
常用实现类
-
HashMap
基于哈希表的实现的Map接口。 该实现提供了所有可选的映射操作,并允许null值和null密钥。 ( HashMap类大致相当于Hashtable ,除了它是不同步的,并允许null)。这个类不能保证地图的顺序; 特别是,它不能保证订单在一段时间内保持不变。特点 I、底层是哈希表:查询速度特快 II、无序集合,存储元素与取出元素的顺序可能不同 III、是一个线程不安全的集合,多线程集合
1.1 HashMap子类LinkedHashMap
哈希表和链表实现的Map接口,具有可预测的迭代顺序。 该实现与HashMap不同之处在于它保持双向链接列表的所有条目。 此链接列表定义迭代排序,通常是将键插入到地图( 插入顺序 )中的顺序 。 请注意,如果将键重新插入到地图中,则插入顺序不受影响。 (A键k被重新插入到地图m如果m.put(k, v)时被调用m.containsKey(k)将返回true之前立即调用。)I、底层是哈希表+链表(保证迭代的顺序) II、是有序的集合
HashMap<String,String> map = new HashMap<>();
map.put("a","a1");
map.put("c","c1");
map.put("b","b1");
map.put("d","d1");
System.out.println(map); // {a=a1, b=b1, c=c1, d=d1}
LinkedHashMap<String,String> link=new LinkedHashMap<>();
link.put("a","a1");
link.put("c","c1");
link.put("b","b1");
link.put("d","d1");
System.out.println(link); // {a=a1, c=c1, b=b1, d=d1}
-
Hashtable
2.1 概述:
该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键值或值。
要从哈希表成功存储和检索对象,用作键的对象必须实现hashCode方法和equals方法。
Hashtable一个实例有两个参数影响其性能: 初始容量和负载因子 。 容量是哈希表中的桶数, 初始容量只是创建哈希表时的容量。 请注意,哈希表是打开的 :在“哈希冲突”的情况下,单个存储桶存储多个条目,必须依次搜索。 负载因子是在容量自动增加之前允许哈希表得到满足的度量。初始容量和负载因子参数仅仅是实现的暗示。 关于何时以及是否调用rehash方法的具体细节是依赖于实现的。
从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Hashtable被同步。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable 。 如果需要线程安全高度并发的实现,那么建议使用ConcurrentHashMap代替Hashtable 。2.2 特点
I、底层是哈希表,是一个线程安全的集合,是单线程集合,速度慢 II、不能存储null值和null键
HashMap<String, String> map = new HashMap<>(); map.put(null,null); System.out.println(map);// {null=null} Hashtable<String, String> table = new Hashtable<>(); table.put(null,"dddd"); // 报错:NullPointerException table.put("dd",null); // 报错:NullPointerException
常用方法
- put(K key, V value),将指定的值与该映射中的指定键相关联(可选操作)。
/*
返回值:
key不重复,返回null
key重复,返回被替换的value值
*/
Map<String,String> map=new HashMap<>();
map.put("a","a1");
map.put("b","b1");
System.out.println(map); // {a=a1, b=b1}
System.out.println(map.put("b","b2")); // b1
System.out.println(map); // {a=a1, b=b2}
System.out.println(map.put("c","c1")); // null
System.out.println(map); // {a=a1, b=b2, c=c1}
- get(Object key),返回指定键映射到的值,如果此映射不包含该键的映射,则返回 null 。
System.out.println(map.get("a")); // a1
System.out.println(map.get("d")); // null
- remove(Object key),如果存在(从可选的操作),从该地图中删除一个键的映射。
System.out.println(map.remove("c")); // c1
System.out.println(map.remove("d")); // null
System.out.println(map); // {a=a1, b=b2}
- containsKey(Object key),如果此映射包含指定键的映射,则返回 true 。.
System.out.println(map.containsKey("b")); // true
System.out.println(map.containsKey("c")); // false
- entrySet(),返回此地图中包含的映射的Set视图。
System.out.println(map.entrySet()); // [a=a1,b=b1]
- keySet(),返回此地图中包含的键的Set视图。
System.out.println(map.keySet()); // [a,b]
- get(Object key),返回指定键映射到的值,如果此映射不包含该键的映射,则返回 null 。
System.out.println(map.get("a")); // a1
System.out.println(map.get("d")); // null
Entry键值对对象
- 概述:在Map接口中有一个内部接口Entry。当Map集合中创建,就会在Map集合中创建一个Entry对象,用来记录键与值
- Entry对象中的方法
2.1 getKey()获取key值
2.2 getValue() 获取value值
Map<String,Integer> map=new HashMap<>();
map.put("a",1);
map.put("b",2);
map.put("c",3);
var set = map.entrySet();
var it = set.iterator();
while (it.hasNext()){
Map.Entry<String, Integer> next = it.next();
System.out.println(next.getKey());
System.out.println(next.getValue());
}
存储自定义类型键值
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Map<String,Student> map=new HashMap<>();
map.put("beijing",new Student("zhangsan",18));
map.put("shanghai",new Student("lisi",19));
map.put("guangzhou",new Student("wangwu",12));
map.put("beijing",new Student("zhaoliu",8));
// {shanghai=Student{name='lisi', age=19}, guangzhou=Student{name='wangwu', age=12}, beijing=Student{name='zhaoliu', age=8}}
System.out.println(map);
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
// Map集合存储自定义类作为键,那么改类必须重写hashCode()和equals()方法,才能保证键不重复
Map<Person,String> map=new HashMap<>();
map.put(new Person("zhangsan",18),"China");
map.put(new Person("lisi",19),"America");
map.put(new Person("zhangsan",18),"England");
// {Person{name='lisi', age=19}=America, Person{name='zhangsan', age=18}=England}
System.out.println(map);
练习
// 计算一个字符串中每次字符出现次数
// 获取用户输入的字符串
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个字符串");
String str=sc.next();
// 创建Map集合
var map= new HashMap<Character, Integer>();
// 获取每一字符,并存入Map集合中
for (char c:str.toCharArray()){
if(map.containsKey(c)){
Integer value=map.get(c);
value++;
map.put(c,value);
}else{
map.put(c,1);
}
}
// 输出结果
System.out.println(map);