文章目录
一.Map
1.1 概述
-
现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map 接口。
-
我们通过查看 Map 接口描述,发现 Map 接口下的集合与 Collection 接口下的集合,它们存储数据的形式不同
-
Collection接口 定义了 单列集合规范,每次存储一个元素 单个元素–>单身集合Collection
-
Map接口,定义了双列集合的规范,每次存储一对儿元素–>夫妻对集合Map<K,V>,K代表键的类型,V代表值的类型。
- Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。Map 中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
- Collection 中的集合称为单列集合, Map 中的集合称为双列集合。
- 需要注意的是, Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
-
1.2 Map的常用子类
-
通过查看Map接口描述,看到Map有多个子类,这里我们主要讲解常用的HashMap集合、LinkedHashMap集合。
-
HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
-
LinkedHashMap:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的
hashCode()方法、equals()方法。 -
Map集合的特点:
- Map集合是一个双列集合,一个元素由两个值组成(key,value)
- Map集合中key是不允许重复的,value可以重复
- Map集合中key的数据类型和value的数据类型可以相同,也可以不同
- Map集合中key和value是一一对应
- java.util.HashMap<k,v>集合 implements Map<k,v>接口
-
HashMap集合的特点:
-
HashMap底层使用的是哈希表结构
-
jdk1.8之前:数组+单向链表
-
jdk1.8之后:数组+单向链表/数组+红黑树
-
-
是一个无序的集合,存储的元素和取出的元素顺序有可能不一致,自然元素会有序排序
-
1.3 Map的常用方法
-
Map接口中定义了很多方法,常用的如下:
-
public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
/* public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。 key不重复,v返回null,key重复,会使用新的value替换之前的value,返回被替换的value */ //创建Map集合对象 Map<String,String> map = new HashMap<>(); //使用put方法往Map集合中添加元素 String v1 = map.put("杨过", "小龙女"); System.out.println("v1:"+v1);//v1:null String v2 = map.put("郭靖", "黄蓉"); System.out.println("v2:"+v2);//v2:null String v3 = map.put("尹志平","小龙女"); System.out.println("v3:"+v3);//v3:null String v4 = map.put("杨过", "大雕"); System.out.println("v4:"+v4);//v4:小龙女 System.out.println(map);//{杨过=大雕, 尹志平=小龙女, 郭靖=黄蓉}
-
public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
/* public V remove(Object key) 根据指定的key,删除对应的键值对 key存在,返回被删除的value,key不存在,返回null */ Map<String,Integer> map = new HashMap<>(); map.put("a",1); map.put("b",2); map.put("d",4); map.put("c",3); System.out.println(map);//{a=1, b=2, c=3, d=4} Integer v1 = map.remove("c"); System.out.println("v1:"+v1);//v1:3 Integer v2 = map.remove("w"); System.out.println("v2:"+v2);//v2:null int v3 = map.remove("s"); System.out.println("v3:"+v3);//v3:空指针异常,进行了拆箱操作 System.out.println(map);//{a=1, b=2, d=4}
-
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
/* public V get(Object key)通过key获取value,key不存在,value返回null */ Map<String,Integer> map = new HashMap<>(); map.put("a",1); map.put("b",2); map.put("d",4); map.put("c",3); Integer v1 = map.get("a"); System.out.println("v1:"+v1);//v1:1 Integer v2 = map.get("q"); System.out.println("v2:"+v2);//v2:null
-
public Set keySet() : 获取Map集合中所有的键,存储到Set集合中。
-
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
-
public boolean containKey(Object key) :判断该集合中是否有此键。
/* public boolean containKey(Object key):判断该集合中是否有此键。 boolean containsValue(Object value) 判断该集合中是否有此值。 包含:返回true; 不包含:返回false */ Map<String,Integer> map = new HashMap<>(); map.put("a",1); map.put("b",2); map.put("d",4); map.put("c",3); boolean b1 = map.containsKey("d"); System.out.println("b1:"+b1);//b1:true boolean b2 = map.containsKey("e"); System.out.println("b2:"+b2);//b2:false System.out.println(map.containsValue(4));//true System.out.println(map.containsValue(5));//false
-
1.4 Map的遍历
方式1:键找值方式
-
通过元素中的键,获取键所对应的值分析步骤:
-
Set keySet() 返回此映射中包含的键的 Set 视图。
-
实现步骤:
- 使用Map集合中的方法keySet,把所有的key取出来存储到一个Set集合中
- 遍历Set集合,获取Map集合的每一个key
- 使用Map集合中的方法get(key),通过key找到value
HashMap<String,Integer> map = new HashMap<>(); map.put("张三",20); map.put("李四",30); map.put("老王",50); //1.使用Map集合中的方法keySet,把所有的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 : map.keySet()){ Integer value = map.get(key); System.out.println(key+"="+value); }
-
方式2:键值对方式
-
即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
-
Entry键值对对象:
-
我们已经知道, Map 中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在 Map 中是一一对应关系,这一对对象又称做 Map 中的一个 Entry(项) 。 Entry 将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历 Map 集合时,就可以从每一个键值对( Entry )对象中获取对应的键与对应的值。
-
Map的遍历_通过键值对的方式
-
Map集合中的方法:
-
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
-
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。获取了Entry对象 , 表示获取了一对键和值,那么同样Entry中 , 分别提供了获取键和获取值的方法:
-
public K getKey() :获取Entry对象中的键。
-
public V getValue() :获取Entry对象中的值。
-
实现步骤:
- 使用Map集合中的方法entrySet,把所有的Entry对象取出来,存储到一个Set集合中
- 遍历Set集合,获取每一个Entry对象
- 使用Entry对象中的方法getKey和getValue获取键与值
HashMap<String,Integer> map = new HashMap<>(); map.put("张三",20); map.put("李四",30); map.put("老王",50); //1.使用Map集合中的方法entrySet,把所有的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遍历Set集合 for (Map.Entry<String, Integer> entry : set) { //3.使用Entry对象中的方法getKey和getValue获取键与值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); }
-
操作步骤与图解:
- 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示: entrySet() 。
- 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
- 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示: getkey() getValue()
- Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了
-
1.5 HashMap存储自定义类型
- HashMap集合如何保证key不重复
- 作为Key元素必须重写hashCode和equals方法
/*
HashMap存储自定义类型
key:Person
value:String
Person类需要重写hashCode和equals方法,以保证同名同年龄的是视为同一个人
*/
//创建HashMap集合,存储Person
HashMap<Person,String> map = new HashMap<>();
map.put(new Person("小明",18),"中国");
map.put(new Person("汤姆",18),"英国");
map.put(new Person("阿里",6),"朝鲜");
map.put(new Person("普哥",28),"俄罗斯");
map.put(new Person("女王",18),"毛里求斯");
//遍历Map集合,使用entrySet+增强for
Set<Map.Entry<Person, String>> set = map.entrySet();
for (Map.Entry<Person, String> entry : set) {
//使用Entry对象中的方法getKey和getValue获取键与值
Person key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"-->"+value);
}
1.6 LinkedHashMap介绍
-
在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。
-
java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
LinkedHashMap特点
LinkedHashMap底层是哈希表+单向链表–>双向链表
LinkedHashMap里边存储的元素是有序的HashMap<String,String> map = new HashMap<>(); map.put("aaa","111"); map.put("ccc","222"); map.put("ddd","333"); map.put("bbb","444"); map.put("aaa","555"); System.out.println(map);//key不允许重复,value可以重复;是一个无序的集合 {aaa=555, ccc=222, bbb=444, ddd=333} LinkedHashMap<String,String> linked = new LinkedHashMap<>(); linked.put("aaa","111"); linked.put("ccc","222"); linked.put("ddd","333"); linked.put("bbb","444"); linked.put("aaa","555"); System.out.println(linked);//key不允许重复,value可以重复;是一个有序的集合 {aaa=555, ccc=222, ddd=333, bbb=444}
-
HashTable(了解即可)
- java.util.Hashtable<k,v>集合 implements Map<k,v>
- Hashtable是一个最早期的双列集合,在jdk1.0版本就存在了
- Hashtable底层也是一个哈希表
- java.util.Hashtable<k,v>集合 implements Map<k,v>
- Hashtable是一个线程安全的集合,是一个单线程的集合,效率低
- HashMap是一个线程不安全的集合,是一个多线程的集合,效率高
- Hashtable不允许存储null键null值,会抛出异常
- HashMap允许存储null键null值
- Hashtable效率低所以被HashMap个取代了,但是Hashtable的子类Properties集合依然活跃在历史的舞台
HashMap<String,String> map = new HashMap<>();
map.put(null,"a");//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.7 Map集合练习
-
练习_计算一个字符串中每个字符出现的次数
-
分析:
-
使用Scanner获取一个字符串
-
定义一个HashMap集合key存储每种字符,value存储字符个数
-
遍历字符串,获取字符串中的每一个字符
- 可以使用String类中的方法toCharArray,把字符串转换为一个字符数组
- 可以使用String类中的方法charAt(i)+length()
-
使用获取到字符,根据Map集合的方法containKey,判断Map集合中是否包含指定的字符
- true:字符存在
- 使用Map集合中的get方法,根据key获取value(字符个数)
- value++
- put(字符,value)
- false:字符不存在
- put(字符,1)
- true:字符存在
-
遍历集合,查看结果
//1.使用Scanner获取一个字符串 System.out.println("请输入一个字符串:"); String s = new Scanner(System.in).next(); //2.定义一个HashMap集合key存储每种字符,value存储字符个数 HashMap<Character,Integer> map = new HashMap<>(); //3.遍历字符串,获取字符串中的每一个字符 for(int i=0; i<s.length(); i++){ char key = s.charAt(i); //4.使用获取到字符,根据Map集合的方法containKey,判断Map集合中是否包含指定的字符 if(map.containsKey(key)){ //true:字符存在 //a.使用Map集合中的get方法,根据key获取value(字符个数) Integer value = map.get(key); //b.value++ //c.put(字符,value) map.put(key,++value); }else{ //false:字符不存在 put(字符,1) map.put(key,1); } } //5.遍历集合,查看结果 System.out.println(map);
-
二.补充知识点
2.1 可变参数
-
是JDK 1.5 之后出现的,底层是一个数组,会根据传递的参数个数不同,而创建长度不同的数组,来接收这些参数
-
作用:当我们定义方法的时候,方法参数的数据类型已经确定了,但是参数的个数不确定,就可以使用可变参数
-
格式:
修饰符 返回值类型 方法名(数据类型...变量名){ 方法体 }
-
可变参数的底层原理:
- 数据类型…变量名:传递参数的个数可以是任意个(0,1,2,…n)
/* 定义一个计算n个int类型整数和的方法 方法参数的数据类型已经确定就是int类型 但是参数的个数不确定,所以就可以使用可变参数 getSum();就会创建一个长度为0的数组,存储参数 int[] arr = new int[]{}; getSum(10);就会创建一个长度为1的数组,存储参数 int[] arr = new int[]{10}; getSum(10,20);就会创建一个长度为2的数组,存储参数 int[] arr = new int[]{10,20}; getSum(10,20,30,40,50,60,70,80,90,100);就会创建一个长度为10的数组,存储参数 int[] arr = new int[]{10,20,30,40,50,60,70,80,90,100}; */ public static int getSum(int...arr){ //System.out.println(arr);//[I@50cbc42f 数组地址值 //System.out.println(arr.length); //定义一个变量,初始值为0,记录累加求和 int sum = 0; //遍历数组,获取数组中的每一个元素 for (int i : arr) { //累加求和 sum += i; } //把和返回 return sum; }
-
可变产生的注意事项:
-
一个参数列表中,只能包含一个可变参数
public static void method(int...a,String...b){}
-
参数列表中有多个参数,可变参数需要写在末尾
public static void method(int a,String s,double d,String...arr){}
-
可变参数的终极写法
public static void method(Object...o){}
-
2.2 Idea开发工具的Debug追踪
-
debug:断点调试
-
作用:
- 可以查看程序执行的流程
- 可以调试程运行过程中出现的一些问题
-
使用步骤:
- 在行号的右边,鼠标左键单击增加一个断点(每个方法的第一行)
- 右键–>debug
- 程序就会停止到添加的第一个断点处
-
快捷键:
- f8:逐行执行代码
- f7:进入到方法中
- shift+f8:跳出方法
- f9:跳到下一个断点
- ctry+f2:停止debug,也会停止程序
- console:切换到控制台(控制台打印的内容)
- variables:显示程序中所有变量的区域
-
2.3 静态导入
-
JDK1.5新特性,静态导入
- 减少开发的代码量
- 标准写法,导入包的时候才能使用
- import static java.lang.System.out; 最末尾,必须是一个静态成员
import java.util.ArrayList; import static java.lang.System.out; import static java.util.Collections.sort; public class Demo01StaticImport { public static void main(String[] args) { System.out.println(1); System.out.println(1); System.out.println(1); out.println(1);//静态导入 out.println(1); out.println(1); ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(3); list.add(2); sort(list);//静态导入 System.out.println(list); } }
2.4 集合嵌套
-
集合嵌套
- 集合中的元素仍是一个集合
- 集合可以随意嵌套(Map可以嵌套Collection,Collection也可以嵌套Map)
//定义一个存储省份名称和城市的Map集合 Map<String,ArrayList<String>> map = new HashMap<>(); //创建存储城市的ArrayList集合 ArrayList<String> hb = new ArrayList<>(); hb.add("石家庄"); hb.add("张家口"); hb.add("承德"); hb.add("邯郸"); ArrayList<String> nmg = new ArrayList<>(); nmg.add("呼和浩特"); nmg.add("包头"); nmg.add("锡林郭勒"); nmg.add("赤峰"); ArrayList<String> gd = new ArrayList<>(); gd.add("深圳"); gd.add("广州"); gd.add("东莞"); gd.add("中山"); //把省份名称和存储城市的ArrayList集合添加到Map中 map.put("河北省",hb); map.put("内蒙古",nmg); map.put("广东省",gd); //遍历Map集合:keySet+增强for Set<String> set = map.keySet(); for (String key : set) { //通过key获取value ArrayList<String> value = map.get(key); //继续遍历ArrayList集合 System.out.println(key+"-->"+value); // for (String cityName : value) { // System.out.println(key+"-->"+cityName); // } } /*打印出: 内蒙古-->[呼和浩特, 包头, 锡林郭勒, 赤峰] 广东省-->[深圳, 广州, 东莞, 中山] 河北省-->[石家庄, 张家口, 承德, 邯郸] */