java:Map
第一章 Map集合
1.1 概述
现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即`java.util.Map`接口。
我们通过查看`Map`接口描述,发现`Map`接口下的集合与`Collection`接口下的集合,它们存储数据的形式不同,如下图。
`Collection`中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
`Map`中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
`Collection`中的集合称为单列集合,`Map`中的集合称为双列集合。
* 需要注意的是,`Map`中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
1.2 Map常用子类
通过查看Map接口描述,看到Map有多个子类,这里我们主要讲解常用的HashMap集合、LinkedHashMap集合。
* **HashMap<K,V>**:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
* **LinkedHashMap<K,V>**:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
> tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。
>
1.3 Map接口中的常用方法
Map接口中定义了很多方法,常用的如下:
* `public V put(K key, V value)`: 把指定的键与指定的值添加到Map集合中。
* `public V remove(Object key)`: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
* `public V get(Object key)` 根据指定的键,在Map集合中获取对应的值。
* `boolean containsKey(Object key) ` 判断集合中是否包含指定的键。
* `public Set<K> keySet()`: 获取Map集合中所有的键,存储到Set集合中。
* `public Set<Map.Entry<K,V>> entrySet()`: 获取到Map集合中所有的键值对对象的集合(Set集合)。
java.util.Map<k,v>集合
Map集合的特点:
1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
3.Map集合中的元素,key是不允许重复的,value是可以重复的
4.Map集合中的元素,key和value是一一对应
*
java.util.HashMap<k,v>集合 implements Map<k,v>接口
HashMap集合的特点:
1.HashMap集合底层是哈希表:查询的速度特别的快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表|红黑树(链表的长度超过8):提高查询的速度
2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
*
java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
LinkedHashMap的特点:
1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
@Test public void MapTestDemo2(){ /* * Map接口中定义了很多方法,常用的如下: public V put(K key, V value)`: 把指定的键与指定的值添加到Map集合中。 public V remove(Object key)`: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。 public V get(Object key)` 根据指定的键,在Map集合中获取对应的值。 boolean containsKey(Object key) ` 判断集合中是否包含指定的键。 public Set<K> keySet()`: 获取Map集合中所有的键,存储到Set集合中。 public Set<Map.Entry<K,V>> entrySet()`: 获取到Map集合中所有的键值对对象的集合(Set集合)。 * */ //创建Map集合对象 Map<String,Integer>map=new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); /* boolean containsKey(Object key) 判断集合中是否包含指定的键。 包含返回true,不包含返回false */ boolean b1 = map.containsKey("赵丽颖"); System.out.println("b1:"+b1);//b1:true boolean b2 = map.containsKey("赵颖"); System.out.println("b2:"+b2);//b2:false }
@Test public void MapTestDemo3(){ /* public V get(Object key) 根据指定的键,在Map集合中获取对应的值。 返回值: key存在,返回对应的value值 key不存在,返回null * */ Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); Integer v1 = map.get("杨颖"); System.out.println("v1:"+v1);//v1:165 Integer v2 = map.get("迪丽热巴"); System.out.println("v2:"+v2);//v2:null }
@Test public void MapTestDemo4(){ /* public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。 返回值:V key存在,v返回被删除的值 key不存在,v返回null */ //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); System.out.println(map);//{林志玲=178, 赵丽颖=168, 杨颖=165} System.out.println("==============================="); Integer v1 = map.remove("林志玲"); System.out.println("v1:"+v1);//v1:178 System.out.println(map);//{赵丽颖=168, 杨颖=165} //int v2 = map.remove("林志颖");//自动拆箱 NullPointerException Integer v2 = map.remove("林志颖"); System.out.println("v2:"+v2);//v2:null System.out.println(map);//{赵丽颖=168, 杨颖=165} }
/* public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。 返回值:v 存储键值对的时候,key不重复,返回值V是null 存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值 */ @Test public void MapTestDemo5(){ //创建Map集合对象多态 Map<String,String> map = new HashMap<>(); String v1 = map.put("李晨", "范冰冰1"); System.out.println("v1:"+v1);//v1:null String v2 = map.put("李晨", "范冰冰2"); System.out.println("v2:"+v2);//v2:范冰冰1 System.out.println(map);//{李晨=范冰冰2} map.put("冷锋","龙小云"); map.put("杨过","小龙女"); map.put("尹志平","小龙女"); System.out.println(map);//{杨过=小龙女, 尹志平=小龙女, 李晨=范冰冰2, 冷锋=龙小云} //注意是无序的 }
Map集合的第一种遍历方式:通过键找值的方式
Map集合中的方法:
Set<K> keySet() 返回此映射中包含的键的 Set 视图。
实现步骤:
1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
2.遍历set集合,获取Map集合中的每一个key
3.通过Map集合中的方法get(key),通过key找到value
public static void main(String[] args) { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); //1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中 Set<String> set = map.keySet(); //2.遍历set集合,获取Map集合中的每一个key //使用迭代器遍历Set集合 Iterator<String> it = set.iterator(); while (it.hasNext()){ String key = it.next(); //3.通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key+"="+value); } System.out.println("-------------------"); //使用增强for遍历Set集合 for(String key : set){ //3.通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key+"="+value); } System.out.println("-------------------"); //使用增强for遍历Set集合 for(String key : map.keySet()){ //3.通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key+"="+value); } }
Map集合遍历的第二种方式:使用Entry对象遍历
Map集合中的方法:
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
实现步骤:
1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey()和getValue()获取键与值
public static void main(String[] args) { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中 Set<Map.Entry<String, Integer>> set = map.entrySet(); //2.遍历Set集合,获取每一个Entry对象 //使用迭代器遍历Set集合 Iterator<Map.Entry<String, Integer>> it = set.iterator(); while(it.hasNext()){ Map.Entry<String, Integer> entry = it.next(); //3.使用Entry对象中的方法getKey()和getValue()获取键与值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } System.out.println("-----------------------"); for(Map.Entry<String,Integer> entry:set){ //3.使用Entry对象中的方法getKey()和getValue()获取键与值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } }
HashMap存储自定义类型键值
Map集合保证key是唯一的:
作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
import java.util.HashMap; import java.util.Map; import java.util.Set; /* HashMap存储自定义类型键值 Map集合保证key是唯一的: 作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一 */ public class Demo01HashMapSavePerson { public static void main(String[] args) { show02(); } /* HashMap存储自定义类型键值 key:Person类型 Person类就必须重写hashCode方法和equals方法,以保证key唯一 value:String类型 可以重复 */ private static void show02() { //创建HashMap集合 HashMap<Person,String> map = new HashMap<>(); //往集合中添加元素 map.put(new Person("女王",18),"英国"); map.put(new Person("秦始皇",18),"秦国"); map.put(new Person("普京",30),"俄罗斯"); map.put(new Person("女王",18),"毛里求斯"); //使用entrySet和增强for遍历Map集合 Set<Map.Entry<Person, String>> set = map.entrySet(); for (Map.Entry<Person, String> entry : set) { Person key = entry.getKey(); String value = entry.getValue(); System.out.println(key+"-->"+value); } } /* HashMap存储自定义类型键值 key:String类型 String类重写hashCode方法和equals方法,可以保证key唯一 value:Person类型 value可以重复(同名同年龄的人视为同一个) */ private static void show01() { //创建HashMap集合 HashMap<String,Person> map = new HashMap<>(); //往集合中添加元素 map.put("北京",new Person("张三",18)); map.put("上海",new Person("李四",19)); map.put("广州",new Person("王五",20)); map.put("北京",new Person("赵六",18)); //使用keySet加增强for遍历Map集合 Set<String> set = map.keySet(); for (String key : set) { Person value = map.get(key); System.out.println(key+"-->"+value); } } }
import java.util.Objects; public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; 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 && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, 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; }
java.util.LinkedHashMap<K,V> entends HashMap<K,V>
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
底层原理:
哈希表+链表(记录元素的顺序)
/* java.util.LinkedHashMap<K,V> entends HashMap<K,V> Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。 底层原理: 哈希表+链表(记录元素的顺序) */ public class Demo01LinkedHashMap { public static void main(String[] args) { HashMap<String,String> map = new HashMap<>(); map.put("a","a"); map.put("c","c"); map.put("b","b"); map.put("a","d"); System.out.println(map);// key不允许重复,无序 {a=d, b=b, c=c} LinkedHashMap<String,String> linked = new LinkedHashMap<>(); linked.put("a","a"); linked.put("c","c"); linked.put("b","b"); linked.put("a","d"); System.out.println(linked);// key不允许重复,有序 {a=d, c=c, b=b} } }
java.util.Hashtable<K,V>集合 implements Map<K,V>接口
Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
HashMap集合(之前学的所有的集合):可以存储null值,null键
Hashtable集合,不能存储null值,null键
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
Hashtable的子类Properties依然活跃在历史舞台
Properties集合是一个唯一和IO流相结合的集合
import java.util.HashMap; import java.util.Hashtable; /* java.util.Hashtable<K,V>集合 implements Map<K,V>接口 Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢 HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快 HashMap集合(之前学的所有的集合):可以存储null值,null键 Hashtable集合,不能存储null值,null键 Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了 Hashtable的子类Properties依然活跃在历史舞台 Properties集合是一个唯一和IO流相结合的集合 */ public class Demo02Hashtable { public static void main(String[] args) { HashMap<String,String> map = new HashMap<>(); map.put(null,"a"); map.put("b",null); map.put(null,null); System.out.println(map);//{null=null, b=null} Hashtable<String,String> table = new Hashtable<>(); //table.put(null,"a");//NullPointerException //table.put("b",null);//NullPointerException table.put(null,null);//NullPointerException } }
练习:
计算一个字符串中每个字符出现次数
分析:
1.使用Scanner获取用户输入的字符串
2.创建Map集合,key是字符串中的字符,value是字符的个数
3.遍历字符串,获取每一个字符
4.使用获取到的字符,去Map集合判断key是否存在
key存在:
通过字符(key),获取value(字符个数)
value++
put(key,value)把新的value存储到Map集合中
key不存在:
put(key,1)
5.遍历Map集合,输出结果
import java.util.HashMap; import java.util.Scanner; /* 练习: 计算一个字符串中每个字符出现次数 分析: 1.使用Scanner获取用户输入的字符串 2.创建Map集合,key是字符串中的字符,value是字符的个数 3.遍历字符串,获取每一个字符 4.使用获取到的字符,去Map集合判断key是否存在 key存在: 通过字符(key),获取value(字符个数) value++ put(key,value)把新的value存储到Map集合中 key不存在: put(key,1) 5.遍历Map集合,输出结果 */ public class Demo03MapTest { public static void main(String[] args) { //1.使用Scanner获取用户输入的字符串 Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String str = sc.next(); //2.创建Map集合,key是字符串中的字符,value是字符的个数 HashMap<Character,Integer> map = new HashMap<>(); //3.遍历字符串,获取每一个字符 for(char c :str.toCharArray()){ //4.使用获取到的字符,去Map集合判断key是否存在 if(map.containsKey(c)){ //key存在 Integer value = map.get(c); value++; map.put(c,value); }else{ //key不存在 map.put(c,1); } } //5.遍历Map集合,输出结果 for(Character key :map.keySet()){ Integer value = map.get(key); System.out.println(key+"="+value); } } }