1、Map集合
· 现实生活与开发中,我们常会看到这样的一类集合:用户ID与账户信息、学生姓名与考试成绩、IP地址与主机名等,这种一一对应的关系,就称作映射。Java提供了专门的集合框架用来存储这种映射关系的对象,即java.util.Map
接口。
1.1 Map接口概述
Map与Collection并列存在。用于保存具有映射关系的数据:key-value
Collection
集合称为单列集合,元素是孤立存在的(理解为单身)。Map
集合称为双列集合,元素是成对存在的(理解为夫妻)。
Map 中的 key 和 value 都可以是任何引用类型的数据。但常用String类作为Map的“键”。
Map接口的常用实现类:HashMap
、LinkedHashMap
、TreeMap
和`Properties
。其中,HashMap是 Map 接口使用频率最高的实现类。
1.2 Map中key-value特点
这里主要以HashMap为例说明。HashMap中存储的key、value的特点如下:
Map 中的 key用Set来存放,不允许重复
,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value,不同key对应的value可以重复
。value所在的类要重写equals()方法。
key和value构成一个entry。所有的entry彼此之间是无序的、不可重复的
。
1.3 Map与Collection集合区别
Collection集合
单列集合,一次只能添加一个元素
有的是有索引,有的没有索引
有的集合可以存储重复的元素,有的则不可以
有的元素是无序的,有的是有序的
Map集合
Map集合是双列集合,由Key和Value组成
Key是不允许重复的,Value是允许重复
Key允许存null值的,但是只能存储唯一的一个
1.4 Map接口中常用的方法
1.4.1 添加、修改操作
public Object put(Object key,Object value)
将指定key-value添加到(或修改)当前map对象中
public void putAll(Map m)
将m中的所有key-value对存放到当前map中
package com.suyv.map;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 10:26
*@Description: Map接口的添加和修改方法
*/
public class MapDemo01 {
// put(Object key,Object value)
@Test
public void Test01(){
Map map = new HashMap();
// 添加数据
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
// 修改数据
map.put("1","红楼梦");
System.out.println(map); // {1=红楼梦, 2=西游记}
}
// putAll(Map m) 添加数据
@Test
public void Test02(){
Map map1 = new HashMap();
// 添加数据
map1.put("1","三国演义");
map1.put("2","西游记");
System.out.println(map1); // {1=三国演义, 2=西游记}
Map map2 = new HashMap();
// 添加数据
map2.put("3","水浒传");
map2.put("4","红楼梦");
System.out.println(map2); // {3=水浒传, 4=红楼梦}
map1.putAll(map2);
System.out.println(map1); // {1=三国演义, 2=西游记, 3=水浒传, 4=红楼梦}
}
// putAll(Map m) 修改数据
@Test
public void Test03(){
Map map1 = new HashMap();
// 添加数据
map1.put("1","三国演义");
map1.put("2","西游记");
System.out.println(map1); // {1=三国演义, 2=西游记}
Map map2 = new HashMap();
// 添加数据
map2.put("1","水浒传");
map2.put("2","红楼梦");
System.out.println(map2); // {1=水浒传, 2=红楼梦}
map1.putAll(map2);
System.out.println(map1); // {1=水浒传, 2=红楼梦}
}
}
1.4.2 删除操作:
public Object remove(Object key)
移除指定key的key-value对,并返回value
public void clear()
清空当前map中的所有数据
package com.suyv.map;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 10:37
*@Description: Map接口的删除方法
*/
public class MapDemo02 {
/*public Object remove(Object key)
移除指定key的key-value对,并返回value
public void clear()
清空当前map中的所有数据*/
// remove(Object key)
// 移除指定key的key-value对,并返回value
@Test
public void Test01(){
Map map = new HashMap();
// 添加数据
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
System.out.println(map.remove("1")); // 三国演义
System.out.println(map); // {2=西游记}
}
// clear()
// 清空当前map中的所有数据
@Test
public void Test02(){
Map map = new HashMap();
// 添加数据
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
map.clear();
System.out.println(map); // {}
}
}
1.4.3 元素查询的操作:
public Object get(Object key)
获取指定key对应的value
public boolean containsKey(Object key)
是否包含指定的key
public boolean containsValue(Object value)
是否包含指定的value
public int size()
返回map中key-value对的个数
public boolean isEmpty()
判断当前map是否为空
public boolean equals(Object obj)
判断当前map和参数对象obj是否相等
package com.suyv.map;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 10:43
*@Description: Map集合中元素查询方法
*/
public class MapDemo03 {
// get(Object key)
// 获取指定key对应的value
@Test
public void Test01(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
System.out.println(map.get("1")); // 三国演义
}
// containsKey(Object key)
// 是否包含指定的key
@Test
public void Test02(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
System.out.println(map.containsKey("1")); // true
System.out.println(map.containsKey("3")); // false
}
// containsValue(Object value)
// 是否包含指定的value
@Test
public void Test03(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
System.out.println(map.containsValue("三国演义")); // true
System.out.println(map.containsKey("红楼梦")); // false
}
// size()
// 返回map中key-value对的个数
@Test
public void Test04(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
System.out.println(map.size()); // 2
}
// isEmpty()
// 判断当前map是否为空
@Test
public void Test05(){
Map map = new HashMap();
System.out.println(map.isEmpty()); // true
map.put("1","三国演义");
map.put("2","西游记");
System.out.println(map); // {1=三国演义, 2=西游记}
System.out.println(map.isEmpty()); // false
}
// equals(Object obj)
// 判断当前map和参数对象obj是否相等
@Test
public void Test06(){
Map map1 = new HashMap();
map1.put("1","三国演义");
map1.put("2","西游记");
System.out.println(map1); // {1=三国演义, 2=西游记}
Map map2 = new HashMap();
map2.put("1","三国演义");
map2.put("2","西游记");
System.out.println(map2); // {1=三国演义, 2=西游记}
System.out.println(map1.equals(map2)); // true
map2.remove("1");
System.out.println(map1.equals(map2)); // false
}
}
1.5 Map集合的遍历
Map集合遍历的方法:
public Set keySet()
返回所有key构成的Set集合
public Collection values()
返回所有value构成的Collection集合
public Set entrySet()
返回所有key-value对构成的Set集合
package com.suyv.map;
import org.junit.Test;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 10:54
*@Description: Map集合的遍历
*/
public class MapDemo04 {
// public Set keySet()
// 返回所有key构成的Set集合
@Test
public void Test01(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
map.put("3","水浒传");
map.put("4","红楼梦");
System.out.println(map); // {1=三国演义, 2=西游记, 3=水浒传, 4=红楼梦}
Set keySet = map.keySet();
for (Object obj : keySet) {
System.out.println(obj); // 1 2 3 4
}
}
// public Collection values()
// 返回所有value构成的Collection集合
@Test
public void Test02(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
map.put("3","水浒传");
map.put("4","红楼梦");
System.out.println(map); // {1=三国演义, 2=西游记, 3=水浒传, 4=红楼梦}
Collection values = map.values();
for (Object obj : values) {
System.out.println(obj); // 三国演义 西游记 水浒传 红楼梦
}
}
// public Set entrySet()
// 返回所有key-value对构成的Set集合
@Test
public void Test03(){
Map map = new HashMap();
map.put("1","三国演义");
map.put("2","西游记");
map.put("3","水浒传");
map.put("4","红楼梦");
System.out.println(map); // {1=三国演义, 2=西游记, 3=水浒传, 4=红楼梦}
Set entrySet = map.entrySet();
for (Object obj : entrySet) {
System.out.println(obj); // 1=三国演义 2=西游记 3=水浒传 4=红楼梦
}
}
}
1.6 Map存储自定义类型元素
需求
每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
要求: 学生姓名相同并且年龄相同视为同一名学生。
编写学生类
package com.suyv.map;
import java.util.Objects;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 11:35
*@Description: 学生实体类Student
*/
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 +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
编写测试类
package com.suyv.map;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 11:37
*@Description: 自定义实体类的测试类
*/
public class MapDemo05 {
@Test
public void Test01(){
//1、创建Map集合对象
Map<Student,String> map = new HashMap<Student,String>();
//2、添加元素
map.put(new Student("lisi",28), "上海");
map.put(new Student("wangwu",22), "北京");
map.put(new Student("zhaoliu",24), "成都");
map.put(new Student("zhouqi",25), "广州");
map.put(new Student("wangwu",22), "南京");
//3、取出元素、键找值方式
Set<Student> keySet = map.keySet();
for(Student key: keySet){
String value = map.get(key);
System.out.println(key.toString()+"....."+value);
// Student{name='zhaoliu', age=24}.....成都
// Student{name='lisi', age=28}.....上海
// Student{name='wangwu', age=22}.....南京
// Student{name='zhouqi', age=25}.....广州
}
}
}
当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须重写对象的hashCode和equals方法。
如果要保证map中存放的key和取出的顺序一致,可以使用java.util.LinkedHashMap集合来存放。
2、Map的主要实现类:HashMap
2.1 HashMap概述
HashMap是 Map 接口使用频率最高的实现类。
HashMap是线程不安全的。允许添加 null 键和 null 值。
存储数据采用的哈希表结构,底层使用数组+单向链表+红黑树进行key-value数据的存储。与HashSet一样,元素的存取顺序不能保证一致。
HashMap 判断两个key相等的标准是:两个 key 的hashCode值相等,通过 equals() 方法返回 true。
HashMap 判断两个value相等的标准是:两个 value 通过 equals() 方法返回 true。
2.2 练习
2.2.1 练习--二级联动
将省份和城市的名称保存在集合中,当用户选择省份以后,二级联动,显示对应省份的地级市供用户选择。
效果演示:
package com.suyv.hashMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 12:08
*@Description: HashMap练习实现二级联动
*/
public class HashMapDemo01 {
public static void main(String[] args) {
Map model = new HashMap();
model.put("北京", new String[] {"北京"});
model.put("上海", new String[] {"上海"});
model.put("天津", new String[] {"天津"});
model.put("重庆", new String[] {"重庆"});
model.put("黑龙江", new String[] {"哈尔滨","齐齐哈尔","牡丹江","大庆","伊春","双鸭山","绥化"});
model.put("吉林", new String[] {"长春","延边","吉林","白山","白城","四平","松原"});
model.put("河北", new String[] {"石家庄","张家口","邯郸","邢台","唐山","保定","秦皇岛"});
model.put("河南", new String[] {"郑州","开封","洛阳","周口","新乡","驻马店","安阳"});
Set keySet = model.keySet();
for(Object s : keySet) {
System.out.print(s + "\t");
}
System.out.println();
System.out.println("请选择你所在的省份:");
Scanner scan = new Scanner(System.in);
String province = scan.next();
String[] citys = (String[])model.get(province);
for(String city : citys) {
System.out.print(city + "\t");
}
System.out.println();
System.out.println("请选择你所在的城市:");
String city = scan.next();
System.out.println("信息登记完毕");
}
}
3、Map实现类之二:LinkedHashMap
LinkedHashMap 是 HashMap 的子类
存储数据采用的哈希表结构+链表结构,在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的先后顺序,可以保证遍历元素时,与添加的顺序一致。
通过哈希表结构可以保证键的唯一、不重复,需要键所在类重写hashCode()方法、equals()方法。
总结:有序,key唯一
package com.suyv.hashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 12:15
*@Description: LinkedHashMap类的使用
*/
public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap();
map.put("王五", 13000.0);
map.put("张三", 10000.0);
//key相同,新的value会覆盖原来的value
//因为String重写了hashCode和equals方法
map.put("张三", 12000.0);
map.put("李四", 14000.0);
//HashMap支持key和value为null值
String name = null;
Double salary = null;
map.put(name, salary);
Set entrySet = map.entrySet();
for (Object obj : entrySet) {
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry);
// 王五=13000.0
// 张三=12000.0
// 李四=14000.0
// null=null
}
}
}
4、Map实现类之三:TreeMap
TreeMap存储 key-value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 key-value 对处于有序状态。
TreeSet底层使用红黑树结构存储数据
TreeMap 的 Key 的排序:
自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。
package com.suyv.hashMap;
import org.junit.Test;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 12:21
*@Description: TreeMap的使用
*/
public class TreeMapDemo {
// 自然排序的使用
@Test
public void Test01(){
TreeMap map = new TreeMap();
map.put("CC",45);
map.put("MM",78);
map.put("DD",56);
map.put("GG",89);
map.put("JJ",99);
Set entrySet = map.entrySet();
for(Object entry : entrySet){
System.out.println(entry);
// CC=45
// DD=56
// GG=89
// JJ=99
// MM=78
}
}
// 自然排序--自定义类
@Test
public void Test02(){
TreeMap map = new TreeMap();
map.put(new User("Tom",12),67);
map.put(new User("Rose",23),"87");
map.put(new User("Jerry",2),88);
map.put(new User("Eric",18),45);
map.put(new User("Tommy",44),77);
map.put(new User("Jim",23),88);
map.put(new User("Maria",18),34);
Set entrySet = map.entrySet();
for(Object entry : entrySet){
System.out.println(entry);
// User{name='Jerry', age=2}=88
// User{name='Tom', age=12}=67
// User{name='Maria', age=18}=34
// User{name='Eric', age=18}=45
// User{name='Rose', age=23}=87
// User{name='Jim', age=23}=88
// User{name='Tommy', age=44}=77
}
}
// 定制排序的使用
@Test
public void Test03(){
//按照User的姓名的从小到大的顺序排列
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return u1.getName().compareTo(u2.getName());
}
throw new RuntimeException("输入的类型不匹配");
}
});
map.put(new User("Tom",12),67);
map.put(new User("Rose",23),"87");
map.put(new User("Jerry",2),88);
map.put(new User("Eric",18),45);
map.put(new User("Tommy",44),77);
map.put(new User("Jim",23),88);
map.put(new User("Maria",18),34);
Set entrySet = map.entrySet();
for(Object entry : entrySet){
System.out.println(entry);
// User{name='Eric', age=18}=45
// User{name='Jerry', age=2}=88
// User{name='Jim', age=23}=88
// User{name='Maria', age=18}=34
// User{name='Rose', age=23}=87
// User{name='Tom', age=12}=67
// User{name='Tommy', age=44}=77
}
}
}
// 定义实体类
class User implements Comparable{
private String name;
private int age;
public User() {
}
public User(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 "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 按照age从小到大的顺序排列,如果age相同,则按照name从大到小的顺序排列
@Override
public int compareTo(Object o) {
if(this == o){
return 0;
}
if(o instanceof User){
User user = (User)o;
int value = this.age - user.age;
if(value != 0){
return value;
}
return -this.name.compareTo(user.name);
}
throw new RuntimeException("输入的类型不匹配");
}
}
5、Map实现类之四:Hashtable
Hashtable是Map接口的古老实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。
Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构(数组+单向链表),查询速度快。
与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。
与HashMap不同,Hashtable 不允许使用 null 作为 key 或 value。
面试题:Hashtable和HashMap的区别
HashMap:底层是一个哈希表(jdk7:数组+链表;jdk8:数组+链表+红黑树),是一个线程不安全的集合,执行效率高
Hashtable:底层也是一个哈希表(数组+链表),是一个线程安全的集合,执行效率低
HashMap集合:可以存储null的键、null的值
Hashtable集合,不能存储null的键、null的值
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了。所以HashMap是Map的主要实现类,Hashtable是Map的古老实现类。
Hashtable的子类Properties(配置文件)依然活跃在历史舞台
Properties集合是一个唯一和IO流相结合的集合
6、Map实现类之五:Properties
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 Properties 中要求 key 和 value 都是字符串类型
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
package com.suyv.hashMap;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 13:05
*@Description: Properties类的使用
*/
public class PropertiesDemo {
@Test
public void Test01() {
Properties properties = System.getProperties();
String fileEncoding = properties.getProperty("file.encoding");//当前源文件字符编码
System.out.println("fileEncoding = " + fileEncoding);
}
@Test
public void Test02() {
Properties properties = new Properties();
properties.setProperty("user","songhk");
properties.setProperty("password","123456");
System.out.println(properties);
}
@Test
public void Test03() throws IOException {
// File file = new File("jdbc.properties");
// System.out.println(file.getAbsoluteFile());
Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);
}
}