第一章 Map集合
1.1 概述
- java.util.Map<k, v>集合
- Map集合的特点:
- Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
- Map集合中的元素,key和value的数据类型可以相同,也可以不同
- Map集合中的元素,key是不允许重复的,value是可以重复的
- Map集合中的元素,key和value是一一对应的
1.2 Map常用的实现类
- java.util.HashMap<k, v>集合 implements ++Map++<k, v>接口
- HashMap集合的特点:
- HashMap集合底层是哈希表:查询速度快
- JDK1.8之前:数组+单向链表
- JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8)–>提高查询速度
- HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
- HashMap集合底层是哈希表:查询速度快
- java.util.LinkedHashMap<k, v>集合 extends ++HashMap++<k, v>集合
- LinkedHashMap集合的特点:
- LinkedHashMap集合底层是哈希表 + 链表:保证迭代的顺序
- LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
1.3 Map接口中的常用方法
- V put(K key, V value)
- 将指定的值与此映射中的指定键关联(可选操作)。
- 注:
- 存储键值对的时候,key不重复,返回值V是null
- 存储键值对的时候,key重复,会使用新的value替换Map中重复的value,返回被替换的【!】value
- V remove(Object key)
- 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
- 注:
- key存在,V返回被删除的值
- key不存在,v返回null
- Integer也可以int接收(自动拆箱),但是如果没有对应key就会空指针异常
- V get(Object key)
- 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
- 注:
- key存在,返回对应value值
- key不存在,v返回null
- boolean containsKey(Object key)
- 如果此映射包含指定键的映射关系,则返回 true。
- 注:
- 包含key存在,true
- 不包含key不存在,false
- Set keySet()
- 返回此映射中包含的键的 Set 视图。
- Set<Map.Entry<K,V>> entrySet()
- 返回此映射中包含的映射关系的 Set 视图。
public class Demo01Map {
public static void main(String[] args) {
//show01();
//show02();
//show03();
show04();
}
//containsKey方法
private static void show04() {
Map<String, Integer> map = new HashMap<>();
map.put("李晨", 185);
map.put("陈赫", 165);
map.put("邓超", 175);
boolean b1 = map.containsKey("李晨");
System.out.println(b1); //true
boolean b2 = map.containsKey("郑凯");
System.out.println(b2); //false
}
//get方法
private static void show03() {
Map<String, Integer> map = new HashMap<>();
map.put("李晨", 185);
map.put("陈赫", 165);
map.put("邓超", 175);
Integer v1 = map.get("陈赫");
System.out.println(v1); //165
}
//remove方法
private static void show02() {
Map<String, Integer> map = new HashMap<>();
map.put("李晨", 185);
map.put("陈赫", 165);
map.put("邓超", 175);
System.out.println(map); //{邓超=175, 陈赫=165, 李晨=185}
Integer v1 = map.remove("邓超"); //int接收也行(自动拆箱),但是如果没有对应key就会空指针异常
System.out.println(v1); //175
System.out.println(map); //{陈赫=165, 李晨=185}
}
//put方法
private static void show01() {
Map<String, String> map = new HashMap<>();
String put1 = map.put("李晨", "范冰冰1");
System.out.println(put1); //null
String put2 = map.put("李晨", "范冰冰2");
System.out.println(put2); //范冰冰1
map.put("冷锋", "龙小云");
System.out.println(map); //{李晨=范冰冰2, 冷锋=龙小云}
}
}
1.4 Map接口遍历键找值方式
- Map集合的第一种遍历方式:通过键找值方式:
- Map无法直接遍历,所以通过键找值得方式
2. Map集合中的方法:Set keySet()
* 返回此映射中包含的键的 Set 视图。
3. 实现步骤:
1. 使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
2. 遍历Set集合,获取Map集合中的每一key
* 迭代器Iterator/增强for
3. 在遍历中,通过Map集合中的方法get(key),找到对应的value
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("李晨", 185);
map.put("陈赫", 165);
map.put("邓超", 175);
//1.keySet
Set<String> setM = map.keySet();
//2.遍历1和get连用
Iterator<String> it = setM.iterator();
while(it.hasNext()){
String key = it.next();
Integer value = map.get(key);
System.out.println("key" + key + "value" + value);
}
System.out.println("========");
//遍历2和get连用
for (String s : setM) {
Integer v = map.get(s);
System.out.println("key=" + s + "value=" + v);
}
}
//结果:
key邓超value175
key陈赫value165
key李晨value185
========
key=邓超value=175
key=陈赫value=165
key=李晨value=185
1.5 Entry键值对对象
- Map.Entry<K, V>:在Map接口中有一个内部接口Entry
- 作用:
- 当Map集合一创建,那么就会在Map集合中创建一个Entry对象,用来记录键与值(键值对对象,键与值得映射关系)
- 可看成:结婚证
- 当Map集合一创建,那么就会在Map集合中创建一个Entry对象,用来记录键与值(键值对对象,键与值得映射关系)
1.6 Map集合遍历键值对方式
- Map集合的第二种遍历方式:使用Entry对象遍历
- Map集合中的方法:
- Set<Map.Entry<K,V>> entrySet()
- 返回此映射中包含的映射关系的 Set 视图。
- Set<Map.Entry<K,V>> entrySet()
- 实现步骤:
- 使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
- 遍历Set集合,获取Map集合中的每一Entry对象
- 迭代器Iterator/增强for
- 在遍历中,使用Entry对象中的方法getKey()和getValue()获取键与值
1.7 HashMap存储自定义类型键值
- Map集合保证key是唯一的:
- 作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
public class Demo01HashMapSavePerson {
public static void main(String[] args) {
///show01();
show02();
}
//HashMap存储自定义类型键值
//key:Person
//value:String
private static void show02() {
HashMap<Person, String> mapH = new HashMap<>();
mapH.put(new Person("张三", 18), "北京");
mapH.put(new Person("李四", 18), "上海");
mapH.put(new Person("张三", 21), "重庆");
mapH.put(new Person("张三", 18), "北京");
//使用entrySet
Set<Map.Entry<Person, String>> setE = mapH.entrySet();
for (Map.Entry<Person, String> pE : setE) {
System.out.println("key=" + pE.getKey() + "value=" + pE.getValue());
}
}
//HashMap存储自定义类型键值
//key:String
//value:Person
private static void show01() {
HashMap<String, Person> mapH = new HashMap<>();
mapH.put("北京", new Person("张三", 18));
mapH.put("上海", new Person("李四", 12));
mapH.put("广州", new Person("王五", 21));
mapH.put("北京", new Person("赵六", 26)); //key与上面重复了,所以会把张三替换掉
//使用keySet——iterator
Set<String> s1 = mapH.keySet();
Iterator<String> it1 = s1.iterator();
while (it1.hasNext()){
String key = it1.next();
Person value = mapH.get(key);
System.out.println("key=" + key + "value" + value);
}
System.out.println("============");
//使用keySet——for
for (String s : s1) {
Person v = mapH.get(s);
System.out.println("key=" + s + "value=" + v);
}
}
}
//结果:
show01()
key=上海valuePerson{name='李四', age=12}
key=广州valuePerson{name='王五', age=21}
key=北京valuePerson{name='赵六', age=26}
============
key=上海value=Person{name='李四', age=12}
key=广州value=Person{name='王五', age=21}
key=北京value=Person{name='赵六', age=26}
show02()
key=Person{name='张三', age=21}value=重庆
key=Person{name='张三', age=18}value=北京
key=Person{name='李四', age=18}value=上海
1.8 LinkedHashMap集合
- java.util.LinkedHashMap<K, V> extends HashMap<K, V>
- (API)Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
- 底层原理:
- 哈希表 + 链接(记录元素的顺序)
- 有序的集合
public class Demo01LinkedHashMap {
public static void main(String[] args) {
//HashMap
HashMap<String, String> mapH = new HashMap<>();
mapH.put("a", "a");
mapH.put("c", "c");
mapH.put("b", "b");
mapH.put("a", "d");
System.out.println(mapH); //无序
//LinkedHashMap
LinkedHashMap<String, String> mapL = new LinkedHashMap<>();
mapL.put("a", "a");
mapL.put("c", "c");
mapL.put("b", "b");
mapL.put("a", "d");
System.out.println(mapL); //有序
}
}
//结果:
{a=d, b=b, c=c}
{a=d, c=c, b=b}
1.9 Hashtable集合
- java.util.Hashtable<K, v>集合 implements Map<K, v>集合
- Hashtable和HashMap区别一:
- Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
- HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程结合,速度快
- Hashtable和HashMap区别二:
- Hashtable集合:不能存储null值,null键
- HashMap集合(之前学的所有集合):能存储null值,null键
- 和Vector(被ArrayList)集合一样,在jdk1.2之后被(HashMap)取代了
- Hashtable集合的子类Properties依然常用
- Properties集合是一个唯一和IO流相结合的集合【将IO时候再来讲解】
1.10 Map集合练习
- 分析:
- 使用Scanner获取用户输入的字符串
- 创建Map集合,key是字符串中的字符,value是字符的个数
- 遍历字符串,获取每一个字符
- 使用获取到的字符,去Map集合判断key是否存在
- key存在:
- 通过字符(key),获取value(字符个数)
- value++
- put(key,value)把新的value存储到Map集合中
- key不存在:
- put(key,1)
- key存在:
- 遍历Map集合,输出结果
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> mapH = new HashMap<>();
//3. 遍历字符串,获取每一个字符
for(char c : str.toCharArray()){
//4. 使用获取到的字符,去Map集合判断key是否存在
if(mapH.containsKey(c)){ //key存在的
Integer value = mapH.get(c);
value++;
mapH.put(c, value);
}else {
mapH.put(c, 1);
}
}
//5. 遍历Map集合,输出结果(这里使用entrySet方法——增强for)
Set<Map.Entry<Character, Integer>> eS = mapH.entrySet();
for (Map.Entry<Character, Integer> e : eS) {
System.out.println("key:" + e.getKey() + " ,value:" + e.getValue());
}
}
//结果:
请输入一个字符串:
aaavvjjjcccc
key:a ,value:3
key:c ,value:4
key:v ,value:2
key:j ,value:3
第二章 补充知识点
2.1 JDK9对集合添加的优化(of方法)
- JDK9的新特性:
- List接口,Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
- static List of (E… elements)
- 使用前提:
- 当集合中存储的元素的个数已经确定了,不在改变时使用
- 注意:
- of方法只适用于List接口,Set接口,Map接口,不适用于接接口的实现类
- of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
- Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常
public class Demo01JDK9 {
public static void main(String[] args) {
List<String> list = List.of("a", "b", "a", "c", "d");
System.out.println(list);//[a, b, a, c, d]
//list.add("w");//UnsupportedOperationException:不支持操作异常
//Set<String> set = Set.of("a", "b", "a", "c", "d");//IllegalArgumentException:非法参数异常,有重复的元素
Set<String> set = Set.of("a", "b", "c", "d");
System.out.println(set);
//set.add("w");//UnsupportedOperationException:不支持操作异常
//Map<String, Integer> map = Map.of("张三", 18, "李四", 19, "王五", 20,"张三",19);IllegalArgumentException:非法参数异常,有重复的元素
Map<String, Integer> map = Map.of("张三", 18, "李四", 19, "王五", 20);
System.out.println(map);//{王五=20, 李四=19, 张三=18}
//map.put("赵四",30);//UnsupportedOperationException:不支持操作异常
}
}
2.2 Debug追踪
- Debug调试程序:
- 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
- 使用方式:
- 在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
- 右键,选择Debug执行程序
- 程序就会停留在添加的第一个断点处
- 执行程序:
- f8:逐行执行程序
- f7:进入到方法中
- shift+f8:跳出方法
- f9:跳到下一个断点,如果没有下一个断点,那么就结束程序
- ctrl+f2:退出debug模式,停止程序
- Console:切换到控制台
第三章 斗地主案例(有序版本)
3.1 案例介绍
3.2 案例分析
3.3 案例实现
public class PokerTest {
public static void main(String[] args) {
//准备牌
//存牌的索引和组装好的牌
HashMap<Integer, String> poker = new HashMap<>();
//存牌的索引
ArrayList<Integer> pokerIndex = new ArrayList<>();
//两个集合(JDK1.9的of)存储花色和牌号(这里用数组)
String[] colors = {"♠", "♥", "♣", "♦"};
String[] numbers = {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3"};
/* ArrayList<String> colors = new ArrayList<>();
colors.add("♠");
colors.add("♥");
colors.add("♣");
colors.add("♦");
ArrayList<String> numbers = new ArrayList<>();
numbers.add("2");
numbers.add("A");
numbers.add("K");
numbers.add("Q");
numbers.add("J");
numbers.add("10");
numbers.add("9");
numbers.add("8");
numbers.add("7");
numbers.add("6");
numbers.add("5");
numbers.add("4");
numbers.add("3");*/
//把大王小王存储到集合中
int index = 0;
poker.put(index, "大王");
pokerIndex.add(index);
index++;
poker.put(index, "小王");
pokerIndex.add(index);
index++;
//循环嵌套来存储52张牌
for (String number : numbers) {
for (String color : colors) {
poker.put(index, number + color);
pokerIndex.add(index);
index++;
}
}
//System.out.println(poker);
//2洗牌,使用Collections中的shuffle方法打乱集合(pokerIndex)
Collections.shuffle(pokerIndex);
//3发牌:定义四个集合
ArrayList<Integer> palyer01 = new ArrayList<>();
ArrayList<Integer> palyer02 = new ArrayList<>();
ArrayList<Integer> palyer03 = new ArrayList<>();
ArrayList<Integer> diPai = new ArrayList<>();
//遍历pokerIndex集合,获取每一个索引
for (int i = 0; i < pokerIndex.size(); i++) {
Integer in = pokerIndex.get(i);
if(i >= 51){//发底牌
diPai.add(in);
}else if(i % 3 == 0){//给玩家一
palyer01.add(in);
}else if(i % 3 == 1){//给玩家二
palyer02.add(in);
}else if(i % 3 == 2){//给玩家三
palyer03.add(in);
}
}
//4排序Collections中的sort
Collections.sort(palyer01);
Collections.sort(palyer02);
Collections.sort(palyer03);
Collections.sort(diPai);
//调用看牌方法lookPoker
lookPoker("刘德华", poker, palyer01);
lookPoker("周润发", poker, palyer02);
lookPoker("周星驰", poker, palyer03);
lookPoker("底牌", poker, diPai);
}
//定义一个看牌方法
// 参数:
// String name:玩家名称
// HashMap<Integer,String> poker:存储牌的poker集合
// ArrayList<Integer> list:存储玩家和底牌的List集合
// 查表法:
// 遍历玩家或者底牌集合,获取牌的索引
// 使用牌的索引,去Map集合中,找到对应的牌
public static void lookPoker(String name, HashMap<Integer, String> poker, ArrayList<Integer> list){
//输出玩家名称:
System.out.println("name:" + name);
//遍历玩家或底牌,获取索引
for (Integer key : list) {
String value = poker.get(key);
System.out.print(value + " ");
}
System.out.println();
}
}
//结果:
name:刘德华
2♦ A♦ K♥ K♦ Q♦ J♠ 10♦ 9♥ 9♦ 8♥ 7♠ 7♥ 7♦ 6♦ 5♦ 4♥ 3♦
name:周润发
2♥ A♠ A♣ K♣ Q♠ Q♥ J♥ J♣ J♦ 10♠ 10♥ 9♠ 8♦ 7♣ 6♠ 6♥ 4♦
name:周星驰
小王 2♠ 2♣ K♠ Q♣ 10♣ 9♣ 8♠ 8♣ 6♣ 5♥ 5♣ 4♠ 4♣ 3♠ 3♥ 3♣
name:底牌
大王 A♥ 5♠