Map集合
PS:map集合中提供一个名词【映射】
映射在数学中的解释:
假设有A,B两个非空集合,如果存在一个法则f,使得A中每个元素按照法则f在B中有唯一确定元素与之对应,则f为从A到B的映射即 f: A->B
上图中映射关系是两个集合的连接,从A集合连接到B集合,这个之间产生的关联就是【映射】
A集合数据如何和B集合数据关联在一起,映射关系提供了一种存储方法**【key-value】 键值对**
映射关系中约束,作为key这一端的集合数据必须是唯一的且不允许重复
作为value这一端的集合数据可以不为唯一且可以重复
PS:就是因为这个原因key是唯一且不重复,所以把存储key这边集合会看做set集合
value是不唯一且可以重复,所以把存储value的这边集合会看做List集合
严格上说Map并不是集合,而是把两个集合之间产生了映射关系(Map接口并没有继承Collection),然而因为Map可以存储数据,所以我们习惯把Map称为集合
因为Map,没有继承Collection所有没有Iterable接口,所以Map集合不提供迭代器遍历,不支持普通for循环也不支持增强for循环
Map集合原则:key必须唯一且不允许重复,value可以不唯一且允许重复
例如:
key1 = value1
key2 = value2
//上面这种结构是允许
//下面这种不允许
key1 = value1
key1 = value2
//此时就违反了key是唯一的原则,所以这样操作是不允许
Map集合的特点1.Map集合中是排重,它是根据key排重 2.key和value的类型必须是引用类型
Map集合的常用实现类HashMap
HashMap
基于哈希表的 Map
接口的实现。此实现提供所有可选的映射操作,并允许使用 null
值和 null
键。(除了非同步和允许使用 null 之外,HashMap
类与 Hashtable
大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap的常用方法
package com.qfedu.Map;
import jdk.nashorn.internal.ir.CallNode;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
/*
HashMap的常用方法
*/
public class HashMapAPIDemo {
public static void main(String[] args) {
//1.创建一个Map集合【默认容量是16 扩展因子0.75】
//它有两个泛型
// 第一个泛型是key的泛型【存储key的数据类型】
// 第二个泛型是value的泛型【存储value的数据类型】
//无论是哪个泛型必须是引用类型
HashMap<String,Integer> map = new HashMap<>();
//它的构造方法中允许指定 初始容量 和 扩展因子
// 它的构造方法还允许 通过传入另外一个map集合对象对当前新建map对象进行初始化【将参中map集合对象kv值进行对创建map初始阿化】
//常用API
//1.向Map集合添加数据
//第一个参数是要存储的key,第二个参数是要存储的value
map.put("key1",1);
//map集合已经重写toString,所以可以直接打印
System.out.println(map);
//编译和运行阶段都不会报错,而且是允许的,map依旧会保持key的唯一性
//put方法的另外一个作用,就是覆盖【修改】对应key存储value
map.put("key1",2);
System.out.println(map);
//Java8中提供了意外一种创建方式
//向map集合集合中添加kv键值对,但是如果key存在则不添加, 否则添加kv键值
map.putIfAbsent("key2",2);
System.out.println(map);
//Java8中提供这个方法的目的就是为了防止存储数据时出现误覆盖的操作
//PS:建议 对map集合存储数据时候建议使用putIfAbsent 修改map中value建议使用put
//将另外一个map集合中的数据添加到当前map结合putAll(map集合对象);
//PS:依旧会根据key进行排重操作
//清空集合 clear 清空集合中的数据,但是集合引用还在
//Java8 compute的方法 提供key在map中进行操作
/*
PS:无论key是否存在后面Lambda表达式都会执行
判断第一个参数 即 key是否存在
1.如果key存在,执行第二参数 lambda表达式 允许获取出key和value的值,直接对取出的这个kv值进行擦操作
这个操作会执行影响原有kv值
2.如果key不存在,执行第二参数 lambda表达式,相当于对map进行赋值
*/
HashMap<String,Integer> map1 = new HashMap<>();
map1.putIfAbsent("1",1);
map1.putIfAbsent("2",1);
map1.putIfAbsent("3",1);
// 返回值是你从操作的value值
Integer compute = map1.compute("3", (k, v) -> v == null ? 0 : v + 1);
System.out.println(compute);
System.out.println(map1);
Integer compute1 = map1.compute("4", (k, v) -> v == null ? 0 : v + 1);
System.out.println(compute1);
System.out.println(map1);
//computeIfAbsent 判断key值执行lambda表达式
/*
如果参数key存在,后面lambda表达式就不会执行
如果参数key不存在,后面lambda表达式就执行,并存储到对应map集合中
*/
map1.computeIfAbsent("3",key -> new Integer(5));
System.out.println(map1);
map1.computeIfAbsent("5",key -> new Integer(5));
System.out.println(map1);
//computeIfPresent 判断key执行对应lambda表达式
/*
1.如果key存在,就执行后面lambda表达式,修改对相应key的value值并得到这个返回值
2.如果key不存在,就不会执行后面lambda表达式,不会像map集合添加元素,返回null
*/
map1.computeIfPresent("3",(k,v)->v+1);
System.out.println(map1);
map1.computeIfPresent("6",(k,v)->v+1);
System.out.println(map1);
//判断map集合中是否存在指定key 存在则true 否则false
boolean key1 = map.containsKey("key1");
//判断map集合中是否存在指定的value 存在则true,否则false
boolean b = map.containsValue(2);
//遍历方式1 forEach方法【这个方法只能打印,不能做操作,没有返回值】
map.forEach((k,v)->{
System.out.println("key的值是:"+k);
System.out.println("value的值是:"+v);
});
//遍历方式2: entrySet 方法会直接返回一个Set集合,这集合中存储是一个个键值对对象
/*
Set<Map.Entry<String, Integer>>
拆解 set<>集合的泛型是 Map.Entry<String,Integer> 对象 Entry对象是一个键值对
*/
Set<Map.Entry<String, Integer>> entries = map1.entrySet();
System.out.println(entries);
for (Map.Entry<String, Integer> entry:entries) {
System.out.println(entry.getKey()+"--》"+entry.getValue());
}
//通过key获取对应value值【如果key不存在这取出一个null】
Integer integer = map.get("key1");
//Java8中提供 getOrDefault 如果key存在着返回对象value值, key不存在返回一个默认值
Integer integer1 = map.getOrDefault("key4", Integer.MIN_VALUE);
System.out.println(integer1);
//判断集合是否为空 isEmpty 判断集合中是否有数据
//获取map集合中所有key值存储到Set集合汇总
Set<String> strings = map.keySet();
//merge 翻译就是合并
//对存在kv进行进行修改并更新kv值
/*
第一个参数是map集合中存在key
第二个参数是赋值给lambda表达式中的第二个参数值
第三个参数是lambda表达式,lambda需要两个参数
参数1 : 是第一个参数key所对应value --> oldVal 就是通过参数key获取的value值
参数2 : 是第二个参数的value -->newVal 第二个参数的值
核心目的就是对这个value值进行操作,然后在重新赋值给对应kv键值
查找map中key为1的value中,并修改value值+10
*/
map1.merge("1",10,(oldVal,newVal)->oldVal+newVal);
map1.merge("2", 20, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer+integer2;
}
});
//删除map集合中的键值对 参数是key值, 只要传入key满足条件 直接删除整个键值对
map.remove("key1");
//判断kv是否存在着删除返回为true 不存在这不删除返回false【少】
boolean remove = map1.remove("6", 6);
//Java8中提供replace方法就是为了替换put概念
map1.replace("2",2); //key存在则替换,不存在着不替换
System.out.println(map1);
//了解 和上面同理
//map1.replace("110",0,110);
//System.out.println(map1);
//通过lambda表达式指定替换条件,替换所有满足条件kv键值对
map1.replaceAll((key,value)->{ //key 和 value 会获取到map集合中每一个键值对
//指定替换条件
if(key.length() >= 1){
value = 1;
}
//这个实现必须有返回值,修改过后的value
return value;
});
System.out.println(map1);
//size 获取集合的长度【一共存储了多少个键值对】
System.out.println(map1.size());
//获取map集合中所有value值存储到Collection集合汇总
Collection<Integer> values = map.values();
}
}
Map集合总结:
Map集合是独立的,它不属于Collection,Map集合提供了一种键值对的存储机制【key-value】
key值必须唯一,value值可以不唯一
Map是接口说以提供实现类:【HashMap、LinkedHashMap、TreeMap】
主要使用实现方式就是HashMap:采用Hash算法, 此时Map中不保证key的添加顺序,保证key的唯一【key保证唯一的方式使用equals和hashcode】
PS:建议Map的key放一个不易改变的数据类型
LinkedHashMap:采用类链表和Hash算法,此时保证Map中key的添加顺序,key不允许重复,它是HashMap的子类,使用方式和HashMap是一样的
Hashtable【不会再开发中使用了】,它也是使用hash算法实现,是HashMap的前身【类似于Vector与ArrayList之间的关系】Hashtable是线程安全效率低,HashMap线程不安全但是效率高
TreeMap:采用红黑二叉树进行存储,TreeMap的排序是排序key而非value,所以需要使用TreeMap进行排序,需要将数据写到key中。
package com.qfedu.Map;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<Integer,String> treeMap = new TreeMap<>();
treeMap.putIfAbsent(10,"1");
treeMap.putIfAbsent(7,"1");
treeMap.putIfAbsent(1,"1");
treeMap.putIfAbsent(11,"1");
treeMap.putIfAbsent(6,"1");
treeMap.putIfAbsent(4,"1");
System.out.println(treeMap);
}
}
key值必须实现了Comparable或Compartor接口
因为整体常用Map都是线程不安全的,所以提供如下修改方式让其线程安全
Collections.synchronizedMap(map对象); //得到一个线程安全的map集合
Set集合作业
1.使用TreeSet实现双色球
package com.qfedu.Set;
import java.util.Random;
import java.util.TreeSet;
/**
* 双色球
* 1、红球随机1-32 唯一且不允许重复并有序输出【升序】
* 2、蓝球随机1-16, 可以红球重复 一个球即可【不参与排序】
*/
public class DoubleBallDemo {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
// Random random = new Random();
while(set.size() < 6){
// int r = random.nextInt(32)+1;
int r = ((int)(Math.random()*32)+1);
set.add(r);
}
System.out.println("红球:"+set);
int b = new Random().nextInt(16)+1;
System.out.println("蓝球:"+b);
}
}
2.向TreeSet集合中加入5个员工的对象,根据员工的年龄(升序)进行排序,若年龄相同,再根据工龄(降序)来排序,若工龄相同,根据薪水(降序)排序
这道题即可以使用Comparable也可以使用Comparator,但是不能两个都使用
PS:
无论是哪个一个接口实现,万能公式 当前对象.属性-传入对象.属性【升序】 传入对象.属性-当前对象.属性【降序】
如果实现Comparable接口那么就使用TreeSet无参构造方法
如果实现Comparator接口那么就是用TreeSet有参构造方法,并将Comparator实现类的对象传入到构造方法
package com.qfedu.Set;
//员工
public class Employee implements Comparable<Employee>{
private String name;
private int age;
private int workerAge;
private int money;
public Employee() {
}
public Employee(String name, int age, int workerAge, int money) {
this.name = name;
this.age = age;
this.workerAge = workerAge;
this.money = money;
}
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;
}
public int getWorkerAge() {
return workerAge;
}
public void setWorkerAge(int workerAge) {
this.workerAge = workerAge;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", workerAge=" + workerAge +
", money=" + money +
'}';
}
@Override
public int compareTo(Employee other) {
// int subAge = this.age - other.age;
// int subWorkerAge = other.workerAge - this.workerAge;
// int subMoney = other.money - this.money;
// int subName = this.name.compareTo(other.name);
//
// return subAge == 0 ? subWorkerAge : subAge;
return this.age - other.age == 0 ?
other.workerAge-this.workerAge == 0 ?
other.money-this.money == 0 ?
this.name.compareTo(other.name):
other.money-this.money :
other.workerAge-this.workerAge :
this.age-other.age;
}
}
package com.qfedu.Set;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet<Employee> set = new TreeSet<>(new Comparator<Employee>() {
// @Override
// public int compare(Employee o1, Employee o2) {
// return 0;
// }
// });
// TreeSet<Employee> set = new TreeSet<>(((o1, o2) -> {
// return o1.getAge()-o2.getAge();
// });
TreeSet<Employee> set = new TreeSet<>();
Collections.addAll(set
,new Employee("张三",18,3,3000)
,new Employee("李四",18,2,5000)
,new Employee("王五",12,10,100000000)
,new Employee("赵六",30,1,4000)
,new Employee("田七",12,10,1));
System.out.println(set);
}
}
3.动物园
package com.qfedu.Set;
public abstract class Animal {
private int BigLongtui;
public Animal(int BigLongtui){
this.BigLongtui = BigLongtui;
}
public int getBigLongtui() {
return BigLongtui;
}
public void setBigLongtui(int bigLongtui) {
BigLongtui = bigLongtui;
}
}
public class QQ extends Animal {
public QQ(int BigLongtui){
super(BigLongtui);
}
@Override
public String toString() {
return "企鹅腿:"+getBigLongtui();
}
}
public class Cat extends Animal {
public Cat(int BigLongtui){
super(BigLongtui);
}
@Override
public String toString() {
return "猫腿:"+getBigLongtui();
}
}
public class Yu extends Animal {
public Yu(int BigLongtui){
super(BigLongtui);
}
@Override
public String toString() {
return "鱼腿:"+getBigLongtui();
}
}
public class TuiException extends Exception {
public TuiException() {
}
public TuiException(String message) {
super(message);
}
public TuiException(String message, Throwable cause) {
super(message, cause);
}
public TuiException(Throwable cause) {
super(cause);
}
public TuiException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
import java.util.Objects;
import java.util.Scanner;
public class AnimalSystem {
public static void main(String[] args) {
System.out.println("-------------------------欢迎来到红浪漫动物园----------------------------");
System.out.println("请输要创建的动物:");
Scanner input = new Scanner(System.in);
String name = input.next();
System.out.println("请输入大长腿的个数:");
int tui = input.nextInt();
Animal animal = null;
try {
animal = createAnimal(name,tui);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
try {
boolean b = checkAnimalTui(animal);
if (b){
System.out.println("恭喜你男宾一位楼上请......");
}
} catch (TuiException e) {
System.out.println(e.getMessage());
}
}
private static boolean checkAnimalTui(Animal animal) throws TuiException {
if(Objects.isNull(animal)){
throw new TuiException("不好意不能用这个退");
}
if( animal instanceof Cat){
Cat cat = (Cat)animal;
if(cat.getBigLongtui() != 4){
throw new TuiException("不好意不能用这个退");
}else{
return true;
}
}else if( animal instanceof QQ) {
QQ qq = (QQ) animal;
if (qq.getBigLongtui() != 2) {
throw new TuiException("不好意不能用这个退");
} else {
return true;
}
}else {
Yu yu = (Yu) animal;
if (yu.getBigLongtui() != 0) {
throw new TuiException("不好意不能用这个退");
} else {
return true;
}
}
}
private static Animal createAnimal(String name, int tui) {
if(name.equals("猫")){
return new Cat(tui);
}else if(name.equals("企鹅")){
return new QQ(tui);
}else if(name.equals("鱼")){
return new Yu(tui);
}else{
throw new RuntimeException("不好意思本动物园没有这个动物");
}
}
}
Map作业
1.一个人对应多张银行卡 Card: cardNo:银行卡号,startDate:开户日期, money:余额
请设计一个程序,输入一个人的名称,得到这个人的所有银行卡信息。 键值对
2.诗词系统
使用Map集合,完成一个图书馆诗词查阅系统,程序运行效果如下:
------------------ 国家图书馆诗词查阅系统 -------------------
请录入您要查看的诗词名:春晓
xxxx,xx,xxxx...
还要继续阅读吗?yes
请录入您要查看的诗词名:沁园春.雪
作者:xxx
朝代:xx
内容:xxx
xxxx,xx,xxxx...
还要继续阅读吗?no
谢谢使用!^-^
Collections工具类
Collections是一个工具类,这个类中封装了多个处理Collection集合中方法
PS:Collections工具类主要是处理List和Set,少部分是处理Map
package com.qfedu.Collections;
import java.util.*;
/*
Collections工具类使用
*/
public class CollectionsAPI {
public static void main(String[] args) {
//Collection有两个子类List和Set,所以只要参数类型是Collection那么list和set都可以使用
//1.向Collection集合中一次添加多个值
List<Integer> list = new ArrayList<>();
Collections.addAll(list,432,5432,5423,62,572,72,16423,624,72,13,6535);
//2.因为在Java8中List集合已经支持sort接口,所以Collections中这个方法的作用就减少
//对List集合进行排序
Collections.sort(list); // 升序
System.out.println(list);
//自定义升降序,需要实现Comparator接口
// Collections.sort(list,(o1,o2)->o2-o1);
// System.out.println(list);
//工具类提供了一个降序的实现【工具类提供了一个方法就方法已经实现了降序】
Collections.sort(list,Collections.reverseOrder());
System.out.println(list);
//Collections工具类提供了二分查找[只能查找List集合]
//找到下标 ,找不到负数
int i = Collections.binarySearch(list, 92);
//交换两个元素的位置
//参数 1. 要交换的List集合 第二、三个参数是要交换位置的下标
Collections.swap(list,0,list.size()-1);
//将集合中元素顺序打乱
Collections.shuffle(list);
//必备技能【多线程并发访问集合】,为了保证线程安全,在Java5之后Collections工具类中提供转换线程全的集合方法
//Collections.synchronizedXXXXXX() --> XXXXXX -->指当前参数属于哪个集合这个XXXX就是哪个集合的父类【List,Set,Map】
/*
以下操作方式之后,得到的都是线程安全的集合,但是他们并不是 Vector,Hashtable ,而是一个线程安全的集合
*/
List<Integer> list1 = Collections.synchronizedList(new ArrayList<Integer>());// SynchronizedRandomAccessList
Set<Integer> set1 = Collections.synchronizedSet(new HashSet<Integer>());
Map<String, Integer> map1 = Collections.synchronizedMap(new HashMap<String, Integer>());
}
}
List作业(斗地主)
package com.qfedu.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
扑克类
*/
public class Poker {
private static final String[] color = {"♥","♦","♠","♣"}; //花色
private static final String[] num = {"2","3","4","5","6","7","8","9","10","J","Q","K","A"};//点数
private static final String[] king = {"大♔","小♕"};//大小王
//提供一个私有属性【存所有的牌】
private static List<String> listPoker = new ArrayList<>();
//提供一个方法进行牌的存储【将牌进行一个组合】
public static void setPoker(){
for (int i= 0;i<color.length;i++){//需要将花色和点数进行拼接
for(int j = 0; j<num.length;j++){
listPoker.add(color[i]+num[j]);
}
if(i<king.length){//i值只要在存储范围内,就可以利用i值当做存储大小王数组的下标
listPoker.add(king[i]);
}
}
}
//洗牌
public static void flushPoker(){
Collections.shuffle(listPoker);
}
//发牌
/*
p1 p2 p3 是三个玩家的手牌
p4 剩余三张手牌
*/
public static void dealPoker(List<String> p1List,List<String> p2List,List<String> p3List,List<String> p4List){
for(int i= 0 ; i<listPoker.size();i++){
//每人一次张
if( i < listPoker.size() -3){
if(i%3 == 0){
p1List.add(listPoker.get(i));
}else if(i % 2 == 0){
p2List.add(listPoker.get(i));
}else{
p3List.add(listPoker.get(i));
}
}else{
p4List.add(listPoker.get(i));//剩余三张牌
}
}
}
}
import java.util.ArrayList;
import java.util.List;
//玩家
public class Player {
//名字
private String name;
//提供一个集合存储手牌
private List<String> pokers = new ArrayList<>();
public Player(){}
public Player(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getPokers() {
return pokers;
}
public void setPokers(List<String> pokers) {
this.pokers = pokers;
}
//展示手牌
public void shoInfosPoker(){
System.out.println(name +":"+pokers);
}
}
import java.util.Comparator;
//排序规则
public class Relu implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
// 花色 点数 和 大小王
//处理的时候 2~10的字符串是最好处理, J Q K A 小王和大王 后一个大于前一个一位数字即可
Integer i1;
Integer i2;
//点数和花色拆开
String str1 = o1.substring(1);//当前对象的值 例如 红2 --》拆分--》 2
String str2 = o2.substring(1);//传入对象的值
if (str1.equals("J")){
i1 = 11;
}else if(str1.equals("Q")){
i1 = 12;
}else if(str1.equals("K")){
i1 = 13;
}else if(str1.equals("A")){
i1 = 14;
}else if(str1.equals("♕")){
i1=15;
}else if(str1.equals("♔")){
i1 = 16;
}else{
i1 = new Integer(str1);
}
if (str2.equals("J")){
i2 = 11;
}else if(str2.equals("Q")){
i2 = 12;
}else if(str2.equals("K")){
i2 = 13;
}else if(str2.equals("A")){
i2 = 14;
}else if(str2.equals("♕")){
i2=15;
}else if(str2.equals("♔")){
i2 = 16;
}else{
i2 = new Integer(str2);
}
return i1-i2;
}
}
package com.qfedu.List;
import com.sun.xml.internal.ws.addressing.WsaActionUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println("---------------------------------JJ斗地主-----------------------------------");
System.out.println("正在洗牌.......");
Poker.setPoker();
Poker.flushPoker();
Thread.sleep(2000);//卡顿效果
Scanner input = new Scanner(System.in);
System.out.println("请输入三个玩家的姓名:");
String name1 = input.next();
String name2 = input.next();
String name3 = input.next();
//创建3个集合存储玩家手牌
List<String> p1List = new ArrayList<>();
List<String> p2List = new ArrayList<>();
List<String> p3List = new ArrayList<>();
//存储最后三张牌
List<String> p4List = new ArrayList<>();
System.out.println("进入发牌阶段");
Poker.dealPoker(p1List,p2List,p3List,p4List);
Thread.sleep(1000);
//需要对牌进行排序操作
Collections.sort(p1List,new Relu());
Collections.sort(p2List,new Relu());
Collections.sort(p3List,new Relu());
//设置玩家
Player p1 = new Player(name1);
Player p2 = new Player(name2);
Player p3 = new Player(name3);
p1.setPokers(p1List);
p2.setPokers(p2List);
p3.setPokers(p3List);
System.out.println("展示玩家手牌:");
p1.shoInfosPoker();
p2.shoInfosPoker();
p3.shoInfosPoker();
System.out.println("请输入要叫地主的玩家名称:");
String name4 = input.next();
System.out.println("展示底牌:"+p4List);
if(p1.getName().equals(name4)){
p1List.addAll(p4List);
Collections.sort(p1List,new Relu());
}else if(p2.getName().equals(name4)){
p2List.addAll(p4List);
Collections.sort(p2List,new Relu());
}else{
p3List.addAll(p4List);
Collections.sort(p3List,new Relu());
}
System.out.println("展示玩家手牌:");
p1.shoInfosPoker();
p2.shoInfosPoker();
p3.shoInfosPoker();
}
}