当前位置: 首页 > news >正文

小黑子—Java从入门到入土过程:第八章

Java零基础入门8.0

  • Java系列第八章
    • 1. 双列集合 Map
      • 1.1 Map 集合中常见的API
      • 1.2 Map 集合的遍历方式
        • 1.2 - I 第一种遍历方式:键找值KeySet 方法
        • 1.2 - II 第二种遍历方式:键值对 entrySet 方法
        • 1.2 - III 第三种遍历方式:lambda表达式
      • 1.3 HashMap
        • 1.3.1 LinkedHashMap
        • 1.3.2 HashMap源码分析
      • 1.4 TreeMap
        • 1.4.1 TreeMap练习
          • 练习一:按照id的降序排列
          • 练习二:按照年龄的升序排列
          • 练习三:统计个数
        • 1.4.2 TreeSet源码分析
      • 1.6 可变参数 ...
      • 1.7 Collections
      • 1.8 综合练习
        • 一、自动点名器1
        • 二、自动点名器2
        • 三、自动点名器3
        • 四、自动点名器4
        • 五、自动点名器5
        • 六、Mao集合案例-省和市
      • 1.9 阶段项目
        • 1.9.1 斗地主游戏1(控制台版)
        • 1.9.2 斗地主游戏2 第一种排序方式
        • 1.9.3 斗地主游戏3 第二种排序方式
      • 1.10 不可变集合
    • 2. Stream流
      • 2.1 初识stream流
      • 2.2 Steam流的中间方法
      • 2.3 Steam流的终结方法
        • 2.3.1 forEach 遍历
        • 2.3.2 count 统计
        • 2.3.3 toArray 收集流数据放进数组
        • 2.3.4 collect 收集流数据放进集合
        • 2.3.5 apply方法
      • 2.4 综合练习
        • 2.4.1 数据过滤
        • 2.4.2 字符串过滤并收集
        • 2.4.3 自定义对象过滤并收集
    • 3.方法引用
      • 3.1 引用静态方法
      • 3.2 引用其他类的成员方法
      • 3.3 引用本类和父类的成员方法
      • 3.4 引用构造方法
      • 3.5 类名引用构造方法
      • 3.6 引用数组的构造方法
      • 3.7 方法引用练习
        • 3.7.1 练习1
        • 3.7.2 练习2
        • 3.7.3 练习3
    • 4.异常
      • 4.1 编译时和运行时异常
      • 4.2 异常在代码中的两个作用
      • 4.3 异常的处理方式
        • 4.3.1 JVM默认的处理方案
        • 4.3.2 捕获异常
            • 4.3.2 - I 如果try中没有遇到问题,怎么执行?
            • 4.3.2 - II 如果try中可能会遇到多个问题,怎么执行?
            • 4.3.2 - III 如果try中遇到的问题没有被捕获,怎么执行?
            • 4.3.2 - IIIV 如果try中遇到了问题,那么try下面的其他代码还会执行吗?
        • 4.3.3 异常中的常见方法
        • 4.3.4 抛出异常
      • 4.4 异常的综合练习
      • 4.5 自定义异常
    • 5. File
      • 5.1 File的构造方法
      • 5.2 File的常见成员方法
        • 5.2.1 判断和获取相关的
        • 5.2.2 创建和删除相关的
        • 5.2.3 获取并遍历相关的
        • 5.2.4 所有遍历并获取的方法
        • 5.2.5 综合练习
          • 练习一:创建文件
          • 练习二:单个文件夹查找文件
          • 练习三:遍历硬盘查找文件夹
          • 练习四:删除文件夹
          • 练习五:统计文件夹大小
          • 练习六:统计各种文件夹数量

Java系列第八章

1. 双列集合 Map

单列集合与双列集合特点的对比:

  • 单列集合每次添加元素的时候,一次只能添加一个元素
  • 双列集合每次添加元素的时候,一次需要添加两个元素,也可以说成是一对元素

在这里插入图片描述
在双列集合中,有两个关键的概念,

在这里插入图片描述
上图左边一列就是键,右边一列就是值
键是不可以重复的,值可以重复

  • 键和值之间是一 一对应的关系,每一个键只能对应自己的值
  • 一个键和值在java就被称为键值对,或者叫做键值对对象(Entry)

下图中就用三个键值对对象:
在这里插入图片描述
双列集合特点的总结:
在这里插入图片描述

1.1 Map 集合中常见的API

Map接口有两个泛型,一个是键的泛型,一个是值的泛型
在这里插入图片描述

在这里插入图片描述

import java.util.HashMap;
import java.util.Map;public class Main {public static void main(String[] args) {//1.创建Map集合的对象Map<String,String> m = new HashMap<>();//2.添加元素 put//put方法的细节// 添加/覆盖//在添加数据的时候,如果键不存在,那么直接吧键值对对象添加到map集合当中,方法返回null//在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把·被覆盖的值进行返回String value1 = m.put("麻瓜", "小老板");System.out.println(value1);System.out.println(m);m.put("愣头青","铁头娃");m.put("石乐志","小青年");System.out.println(m);String value2 = m.put("石乐志", "李在赣神魔");System.out.println("改的是:"+value2);System.out.println(m);//3.删除 removeString result = m.remove("愣头青");System.out.println("删除的是:"+result+"所在的键值");System.out.println(m);//4.清空 clear//m.clear(); 直接为空 {}//5.判断是否包含 containsKeyboolean keyResult = m.containsKey("愣头青");System.out.println(keyResult);boolean res2 = m.containsValue("小老板");System.out.println(res2);boolean res3 = m.isEmpty();//判断是否清空了,如果没有返回fales,反之返回trueSystem.out.println(res3);System.out.println(m.size());//返回双列集合的长度}
}

在这里插入图片描述

1.2 Map 集合的遍历方式

有三种遍历方式

  1. 键找值
  2. 键值对
  3. Lambda表达式

1.2 - I 第一种遍历方式:键找值KeySet 方法

先把所有的键提出到一个单列集合中,然后遍历单列集合,得到每一个键,然后通过get方法得到每个键所对应的值

  1. Map的keySet方法可以把Map集合的所有键存到一个Set集合中
    格式:Map集合名.keySet();,返回值是一个Set集合
    public static void main(String[] args) {Map<String,String>map=new HashMap<>();map.put("one","1");map.put("two","2");map.put("three","3");map.put("four","4");Set<String> keys= map.keySet();		}

在这里插入图片描述

  1. Map的get(Object key)方法可以得到键所对应的值
    格式:Map集合名.get(键名);,返回值是键所对应的值
    public static void main(String[] args) {Map<String,String>map=new HashMap<>();map.put("one","1");map.put("two","2");map.put("three","3");map.put("four","4");sout(map.get("one"));//1

在这里插入图片描述
练习:

在这里插入图片描述

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;public class Main {public static void main(String[] args) {//1.创建Map集合的对象Map<String,String> m = new HashMap<>();//2.添加元素m.put("麻瓜", "小老板");m.put("愣头青","铁头娃");m.put("石乐志","小青年");System.out.println(m);//3.通过键找值//3.1 获取所有的键,把这些键放到一个单列集合当中Set<String> keys = m.keySet();//3.2 通过增强for遍历单列集合,得到每一个键for (String key : keys) {System.out.println(key);}System.out.println("======================");//4.通过迭代器遍历键找值Iterator<String> it = keys.iterator();while (it.hasNext()){String s = it.next();System.out.println(s);System.out.println(s+"="+m.get(s));}System.out.println("======================");//5.通过lambd表达式遍历键找值keys.forEach(s-> System.out.println(s));}
}

在这里插入图片描述

1.2 - II 第二种遍历方式:键值对 entrySet 方法

依次获取Map集合里的每一个键值对对象(Entry),然后用键值对对象(Entry)通过getKey方法获取键,通过getValue方法获取值
上述的Entry实际上是Map接口的一个内部接口

Map的entrySet方法可以把所有的键值对存到一个Set集合中
格式:Map集合名.entry()返回值是一个set集合
注意,这里返回的set集合的泛型是Entry类型,即Set< Map.Entry< E,E > >

//Entry实际上是Map接口的一个内部接口,所以表达Entry时要用Map.来调用一下
Set<Map.Entry<String, String>> entries = map.entrySet();//当然。这里的Map.也可以不写,但是要在代码最上面进行一个导包
import java.util.Map.Entry
Set<Entry<String, String>> entries = map.entrySet();

练习:
在这里插入图片描述

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;public class Main {public static void main(String[] args) {//1.创建Map集合的对象Map<String,String> m = new HashMap<>();//2.添加元素m.put("麻瓜", "小老板");m.put("愣头青","铁头娃");m.put("石乐志","小青年");System.out.println(m);//3.通过键值对对象进行遍历//3.1 通过一个方法获取所有的键值对对象,返回一个set集合Set<Map.Entry<String,String>> entries = m.entrySet();//3.2 遍历entries这个集合,去得到里面的每一个键值对对象//一、增强forSystem.out.println("返回的set集合:"+m.entrySet());for (Map.Entry<String, String> entry : entries) {System.out.println(entry);}System.out.println("======================");//二、迭代器Iterator<Map.Entry<String,String>> it = entries.iterator();while (it.hasNext()){Map.Entry<String,String> en = it.next();System.out.println(en);}System.out.println("======================");//三、lambda表达式entries.forEach((s)-> System.out.println(s));}
}

在这里插入图片描述

1.2 - III 第三种遍历方式:lambda表达式

Map的forEach方法
default void forEach(BiConsumer<? super K, ?super V>action)
可以结合lambda遍历Map集合
BiConsumer是一个函数式接口

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;public class Main {public static void main(String[] args) {//1.创建Map集合的对象Map<String,String> m = new HashMap<>();//2.添加元素//键:人物的名字//值:话m.put("麻瓜", "小老板李在赣神魔");m.put("愣头青","铁头娃李在赣神魔");m.put("石乐志","小青年李在赣神魔");System.out.println(m);//3.利用lambda表达式进行遍历m.forEach(new BiConsumer<String, String>() {@Overridepublic void accept(String key, String value) {System.out.println(key + "=" + value);}});System.out.println("=================================");//底层://forEach其实就是利用第二种方式进行遍历,一次得到每一个键和值//再调用accept方法m.forEach((String key, String value)-> System.out.println(key + "=" + value));}
}

在这里插入图片描述
foreach底层源码:
在这里插入图片描述

import java.util.HashMap;
import java.util.Map;public class Main {public static void main(String[] args) {//1.创建Map集合的对象Map<String,String> m = new HashMap<>();//2.添加元素m.put("麻瓜", "小老板");m.put("愣头青","铁头娃");m.put("石乐志","小青年");System.out.println(m);//Set<Map.Entry<String,String>> entries = m.entrySet();这个方法不用,直接写在foreach冒号右边,就和源码一样了//所以与源码里面相比只是少一个变量而已for (Map.Entry<String, String> entry : m.entrySet()) {String key = entry.getKey();String value = entry.getValue();System.out.println(key+"="+value);}}
}

1.3 HashMap

注意:HashMap的键如果存储的是自定义对象,那么就需要重写hashCode和equals方法

在这里插入图片描述
在底层,创建一个默认长度为16,默认加载因子为0.75的数组,默认初始化值为null,利用put方法添加数据,put方法的底层首先会创建一个Entry对象(Entry1),Entry对象里记录着键和值
在这里插入图片描述

注意:只利用键计算哈希值,跟值无关,即要保证键的唯一

利用哈希值计算出在数组中应存入的索引,
在这里插入图片描述

如果索引处为null,就把数据存入

如果存入位置的值不是null,就表示存入位置有元素,

然后调用equals方法比较的属性值(只比较键的属性值)
在这里插入图片描述

如果键的属性值一样,那么新的Entry对象(Entry2)就会覆盖原来的Entry对象(Entry1),这就是put方法隐藏的覆盖功能
在这里插入图片描述

如果不一样,
jdk8之前,存入时是新元素存入数组,老的元素挂在新元素的下面
在这里插入图片描述

jdk8之后,新元素直接挂在老元素的下面
在这里插入图片描述

额外:jdk8开始,当链表的长度超过8而且数组的长度超过64,链表就会自动转成红黑树

在这里插入图片描述
小结:
在这里插入图片描述
练习一:
在这里插入图片描述
由于他这里题目要求同姓名,同年龄是同一个学生,而学生对象在键的位置,HashMap的底层也是哈希表,所以要想使同姓名,同年龄是同一个学生的话就要在学生类重写hashCode方法,使hashCode方法判断由地址值转变为属性值

核心点:

HashMap的键位置如果存储的是自定义对象,需要重写hashCode和lequals方法

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;public class A01_HashMapDemo1 {public static void main(String[] args) {//1.创建HashMap的对象、HashMap<Student,String> hm = new HashMap<>();//2.创建三个学生对象Student s1 = new Student("zhangsan",23);Student s2 = new Student("magua",24);Student s3 = new Student("lisi",25);Student s4 = new Student("magua",24);//3.添加对象hm.put(s1,"wuhu");hm.put(s2,"guangdoor");hm.put(s3,"xinlimuli");hm.put(s4,"guangdoor");//4.遍历集合Set<Student> keys = hm.keySet();for (Student key : keys) {String value = hm.get(key);System.out.println(key+"="+value);}System.out.println("==========================");//键加值遍历Set<Map.Entry<Student, String>> entries = hm.entrySet();for (Map.Entry<Student, String> entry : entries) {Student key = entry.getKey();String value = entry.getValue();System.out.println(key+"="+value);}System.out.println("==========================");//lambda表达式hm.forEach((Student student, String s)->{System.out.println(student+"="+s);});}
}

在这里插入图片描述

练习二:
在这里插入图片描述
像这种统计计数的,一般都是定义一个计数器
但如果是任意选择自己想去的景点,景点的数量又很多,到底定义多少个计数器才行?

想这种统计的内容比较多,或者压根不知道要统计多少种的时候,此时就可以用Map集合进行统计。

核心:

创建一个Map集合,键为:景点,值为:投票次数,然后判断集合中是否包含该景点,不包含表示第一次出现,包含表示已经出现过了
在这里插入图片描述

import java.util.*;public class A01_HashMapDemo1 {public static void main(String[] args) {//1.需要先让同学们投票//定义一个数组,储存4个景点String[] arr = {"A", "B", "C", "D"};//利用随机数模拟80个同学的投票,并把投票的结果存储起来ArrayList<String> list = new ArrayList<>();Random r = new Random();//生成学生的80个投票for (int i = 0; i < 80; i++) {//利用数组长度,在0~3内生成一个随机的索引int index = r.nextInt(arr.length);list.add(arr[index]);}//2.如果要统计的东西比较多,不方便使用计数器思想//我们可以定义map集合,利用集合进行统计HashMap<String,Integer> hm = new HashMap<>();for (String name : list) {//判断当前的景点在map集合当中是否存在if(hm.containsKey(name)){//如果存在//先获取当前景点已经被投票的次数int count = hm.get(name);//表示当前景点又被投了一次count++;//把新的次数再次添加到集合当中hm.put(name,count);}else{//如果不存在hm.put(name,1);}}//3.求最大值// A ???// B ???// C ???// D ???//那么拿谁做参照物呢?A B C D 都不合适,看它们最小投票必定是0//就拿0做参照物int max = 0;Set<Map.Entry<String, Integer>> entries = hm.entrySet();for (Map.Entry<String, Integer> entry : entries) {Integer count = entry.getValue();if(count>max){max = count;}}System.out.println("最大值:"+max);//4.判断哪个景点的次数跟最大值一样,如果一样,打印出来//千万注意上面3的for循环与4的不能叠加在一起写//只有最大值确定了才能去做比较for (Map.Entry<String, Integer> entry : entries) {Integer count = entry.getValue();if(count==max){System.out.println("投票最多的是:"+entry.getKey()+"-"+max);}}}
}

在这里插入图片描述

1.3.1 LinkedHashMap

无特殊方法,直接使用Map的方法

在这里插入图片描述
即每个元素都会像双向链表的结点一样,除了存储着数据外,还存储着其他元素的地址值,存入的第一个元素就是头结点,最后一个元素就是尾结点,遍历时从头结点开始,按照添加的顺序遍历,到尾结点结束

这样一来得到遍历的数据,就和添加的数据一模一样了

在这里插入图片描述
代码实现:

import java.util.*;public class Main {public static void main(String[] args) {LinkedHashMap<String, Integer> lhm = new LinkedHashMap<>();lhm.put("b", 789);lhm.put("b",111);lhm.put("c", 456);lhm.put("a", 123);lhm.put("a",123);System.out.println(lhm);}
}

1.3.2 HashMap源码分析

选中HashMap,然后ctrl+b快捷切换源码页面
在这里插入图片描述
先了解ieda中源码的一些东西
在这里插入图片描述

在源码中ctrl+f12可以打开一个菜单
在这里插入图片描述
蓝色的圆圈c(class),代表,红色的圆圈m(method)代表着方法
上面四个方法名和类名一样的,就是构造方法,下面一堆就是成员方法

比如这里的afterNodeRemoval就是方法名,(Node< K,V >)就是形参,void就是返回值

在这里插入图片描述

就表示重写的父类或接口中的方法
在这里插入图片描述
表示从父类中继承下来的方法

  • 点击这个灰色方法,那么就会跳转到Map这个接口当中,相当于该方法是继承Map接口里的方法

在这里插入图片描述
黄色的圆圈f(field) 就表示属性值,有可能是成员变量,也有可能是常量
在这里插入图片描述
这些就是HashMap下的内部类
在这里插入图片描述
HashMap里的 Node
在这里插入图片描述

在HashMap中,每一个元素都是一个Node的对象,而且在源码中,Node是Map.Entry的实现类,所以,也称一个Node(键值对)为Entry对象

四个成员变量:

  1. hash:哈希值
  2. key:键
  3. value:值
  4. next:记录链表里下一个元素(结点)的地址值

如果是红黑树,那么红黑树里的结点元素叫做TreeNode:
在这里插入图片描述

TreeNode也是HashMap的内部类,有五个成员变量

  • parent:记录父节点的地址值
    left:记录左子节点的地址值
    right:记录右子节点的地址值
    prev:暂时不了解
    red:判断是红色还是黑色,true为红色,false为黑色

除此之外,红黑树的元素还有

  • hash:哈希值
    key:键
    value:值
    next:记录链表里下一个元素(结点)的地址值

HashMap底层是由数组,链表和红黑树组成

HashMap的成员变量:
table,组成HashMap底层的数组
在这里插入图片描述
里面装的元素就是Node对象

这个常量表达了数组的默认长度是16,位运算,左移1个乘以2,移2个乘以2^2,
移动4个乘以2^4
在这里插入图片描述
这个常量就是默认的加载因子0.75
在这里插入图片描述
HashMap的构造方法
空参构造,将加载因子0.75赋值给loadFactor,此时,底层的数组还没有创建。即使用空参构造创建HashMap集合时,底层还没有数组

在这里插入图片描述

当使用put方法向集合中添加第一个元素时,底层就创建数组了
在这里插入图片描述

  • put方法中的putVal有五个参数,
    hash(key):键的哈希值
    key:键
    value:值
    onlyAbsent:默认为false,表示重复的键所对应的值会被覆盖,为true时表示不被覆盖。
    evict:暂时不用了解

resize方法的作用:

//1.如果当前是第一次添加数据,底层会创建一个默认长度为16,
//加载因子为0.75的数组
//2.如果不是第一次添加数据,会看数组中的元素是否达到了扩容的条件
//如果没有达到扩容条件,底层不会做任何操作
//如果达到了扩容条件,底层会把数组扩容为原先的两倍,
//并把数据(链表或红黑树的数据)全部转移到新的哈希表中

源码详解:

1.看源码之前需要了解的一些内容Node<K,V>[] table   哈希表结构中数组的名字DEFAULT_INITIAL_CAPACITY:   数组默认长度16DEFAULT_LOAD_FACTOR:        默认加载因子0.75HashMap里面每一个对象包含以下内容:
1.1 链表中的键值对对象包含:  int hash;         //键的哈希值final K key;      //键V value;          //值Node<K,V> next;   //下一个节点的地址值1.2 红黑树中的键值对对象包含:int hash;         		//键的哈希值final K key;      		//键V value;         	 	//值TreeNode<K,V> parent;  	//父节点的地址值TreeNode<K,V> left;		//左子节点的地址值TreeNode<K,V> right;	//右子节点的地址值boolean red;			//节点的颜色1.3 数组中的键值对对象分情况讨论如果数组里的元素下挂的是链表那就包含:int hash;         //键的哈希值final K key;      //键V value;          //值Node<K,V> next;   //下一个节点的地址值如果数组里的元素下挂的是红黑树那就包含:int hash;         		//键的哈希值final K key;      		//键V value;         	 	//值TreeNode<K,V> parent;  	//父节点的地址值TreeNode<K,V> left;		//左子节点的地址值TreeNode<K,V> right;	//右子节点的地址值boolean red;			//节点的颜色        				2.添加元素
//空参构造,只是给集合一个加载因子,连数组都没创建
HashMap<String,Integer> hm = new HashMap<>();
//使用put方法后,HashMap集合在底层创建数组
hm.put("aaa" , 111);
hm.put("bbb" , 222);
hm.put("ccc" , 333);
hm.put("ddd" , 444);
hm.put("eee" , 555);添加元素的时候至少考虑三种情况:
2.1数组位置为null
2.2数组位置不为null,键不重复,挂在下面形成链表或者红黑树
2.3数组位置不为null,键重复,元素覆盖//put源码
//参数一:键
//参数二:值//返回值:被覆盖元素的值,如果没有覆盖,返回null
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}//利用键计算出对应的哈希值,再把哈希值进行一些额外的处理
//简单理解:返回值就是返回键的哈希值
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}//参数一:键的哈希值
//参数二:键
//参数三:值
//参数四:如果键重复了是否保留
//		   true,表示老元素的值保留,不会覆盖
//		   false,表示老元素的值不保留,会进行覆盖
//putVal是put方法里调用的方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//定义一个局部变量,用来记录哈希表中数组的地址值。//因为成员变量定义在堆里,而方法运行在栈里,如果不定义数组记录元素的话//会在调用数组里的Node结点时就会反复在堆和栈之间重复运行,//影响效率Node<K,V>[] tab;//临时的第三方变量,用来记录键值对对象的地址值Node<K,V> p;//表示当前数组的长度int n;//表示索引int i;//把哈希表中数组的地址值,赋值给局部变量tabtab = table;if (tab == null || (n = tab.length) == 0){//resize方法的作用//1.如果当前是第一次添加数据,底层会创建一个默认长度为16,//加载因子为0.75的数组//2.如果不是第一次添加数据,会看数组中的元素是否达到了扩容的条件//如果没有达到扩容条件,底层不会做任何操作//如果达到了扩容条件,底层会把数组扩容为原先的两倍,//并把数据(链表或红黑树的数据)全部转移到新的哈希表中tab = resize();//表示把当前数组的长度赋值给nn = tab.length;}//拿着数组的长度跟键的哈希值进行计算,//计算出当前键值对对象,在数组中应存入的位置//这里的i就是index,即元素应存入的索引i = (n - 1) & hash;//获取数组中对应元素的数据p = tab[i];if (p == null){
//添加第一个元素//底层会创建一个键值对对象,直接放到数组当中tab[i] = newNode(hash, key, value, null);}else {
//添加其他元素Node<K,V> e;K k;//等号的左边:数组中键值对的哈希值//等号的右边:当前要添加键值对的哈希值//如果键不一样,此时返回false//如果键一样,返回trueboolean b1 = p.hash == hash;if (b1 && ((k = p.key) == key || (key != null && key.equals(k)))){e = p;} else if (p instanceof TreeNode){//判断数组中获取出来的键值对是不是红黑树中的节点//如果是,则调用方法putTreeVal,//把当前的节点按照红黑树的规则添加到树当中。e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);} else {//如果从数组中获取出来的键值对不是红黑树中的节点//表示此时下面挂的是链表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {//此时就会创建一个新的节点,挂在下面形成链表p.next = newNode(hash, key, value, null);//判断当前链表长度是否超过8,//如果超过8,就会调用方法treeifyBin//treeifyBin方法的底层还会继续判断//判断数组的长度是否大于等于64//如果同时满足这两个条件,就会把这个链表转成红黑树if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash);break;}//e:			  0x0044  ddd  444//要添加的元素: 0x0055   ddd   555//如果哈希值一样,//就会调用equals方法比较内部的属性值是否相同if (e.hash == hash && ((k = e.key)== key || (key != null && key.equals(k)))){break;}p = e;}}//如果e为null,表示当前不需要覆盖任何元素//如果e不为null,表示当前的键是一样的,值会被覆盖//e:0x0044  ddd  555//要添加的元素: 0x0055   ddd   555if (e != null) {V oldValue = e.value;if (!onlyIfAbsent || oldValue == null){//等号的右边:当前要添加的值//等号的左边:0x0044的值//即覆盖的是老的键值对的值,而不是把整个键值对给覆盖e.value = value;}afterNodeAccess(e);return oldValue;}}//threshold:记录的就是数组的长度 * 0.75,//哈希表的扩容时机 即threshold:16 * 0.75 = 12if (++size > threshold){resize();}//表示当前没有覆盖任何元素,返回nullreturn null;}

1.4 TreeMap

无特殊方法,直接使用Map的方法

在这里插入图片描述
TreeMap的两种比较方式(Comparable,comparator)

  1. 在自定义的JavaBean类中实现Comparable接口,指定比较规则
  2. 创建集合时传递Comparator对象,指定比较规则

在这里插入图片描述

1.4.1 TreeMap练习

练习一:按照id的降序排列

在这里插入图片描述

练习一:

import java.util.*;public class Main {public static void main(String[] args) {//1.创建集合对象//Integer Double 默认情况下都是按照升序排列的//String 按照字母在AscII码表种对应的数字升序进行排列//abcdefg...TreeMap<Integer,String> tm = new TreeMap<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {//o1:当前要添加的元素//o2:表示已经在红黑树中存在的元素//返回值如果是负数,则会认为要添加的元素是小的,存在红黑树的左边//返回值如果是正数,则会认为要添加的元素是大的,存在红黑树的右边//返回值如果是0,则会认为要添加的元素已经存在,不存入红黑树return o2 - o1;}});//在需求2中降序排列,第一种方式已经不满足//那么就在第二种方式种,在创建集合对象中传递比较器的对象// 忘记名字,ctrl + p快捷提示tm.put(1,"可乐");tm.put(4,"雪碧");tm.put(3,"橙子");tm.put(2,"雪糕");System.out.println(tm);}
}

在这里插入图片描述

练习二:按照年龄的升序排列

在这里插入图片描述

Student类:
public class Student implements Comparable<Student>{private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}@Overridepublic int compareTo(Student o) {//按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人//this:表示当前要添加的元素//o:表示已经在红黑树中存在的元素//返回值://负数:表示当前要添加的元素是小的,存左边//正数:表示当前要添加的元素是大的,存右边//0:表示当前要添加的元素已经存在,舍弃int i = this.getAge() - o.getAge();i = i == 0 ? this.getName().compareTo(o.getName()) : i;return i;}
}测试类:
import java.util.*;public class A01_HashMapDemo1 {public static void main(String[] args) {//创建集合TreeMap<Student,String> tm = new TreeMap<>();//但是TreeMap是要建立排序的,而如果Student没有建立排序的话,就直接报错//那么就在Student类当中,implement Comparable<E> 重写排序//2.创建三个学生对象Student s1 = new Student("麻瓜",21);Student s2 = new Student("张三",22);Student s3 = new Student("李四",23);//3,添加元素tm.put(s1,"鸡场");tm.put(s2,"guangdoor");tm.put(s3,"xinrimu里");System.out.println(tm);}
}

在这里插入图片描述

练习三:统计个数

在这里插入图片描述

import java.util.StringJoiner;
import java.util.TreeMap;public class Main {public static void main(String[] args) {/*输出结果:a (5)b (4) c (3) d (2) e ( 1)新的统计思想:利用map集合进行统计如果题目中没有要求对结果进行排序,默认使用HashMap如果题目中要求对结果进行排序,请使用TreeMap键:表示要统计的内容值:表示次数*/String s = "aaabcdddcccabebbe";//1.创建集合TreeMap<Character,Integer> tm = new TreeMap<>();//2.遍历字符串得到里面的每一个字符for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);//这是遍历s的每个字符//拿着c到集合中判断是否存在//存在,表示当前字符又出现了一次// 不存在,表示当前字符是第一次出现if(tm.containsKey(c)){//存在//先把已经出现的次数拿出来int count = tm.get(c);//当前字符又出现了一次count++;//把自增之后的结果再添加到集合当中tm.put(c,count);}else{//不存在tm.put(c,1);}}//4.遍历集合,并按照指定的格式进行拼接//第一种方式:StringBuilderStringBuilder sb = new StringBuilder();tm.forEach((key,value)->sb.append(key).append("(").append(value).append(")"));System.out.println(sb);//第二种方式:StringJoinerStringJoiner sj = new StringJoiner("","","");tm.forEach((key,value)->sj.add(key+"").add("(").add(value+"").add(")"));System.out.println(sj);}
}

在这里插入图片描述
总结:
在这里插入图片描述

1.4.2 TreeSet源码分析

TreeMap底层为红黑树,
红黑树每一个元素内部的属性分别为:
在这里插入图片描述

  • key:键
    value:值
    parent:记录父节点的地址值
    left:记录左子节点的地址值
    right:记录右子节点的地址值
    color:判断是红色还是黑色,true为黑色,false为红色,TreeMap红黑树结点的初始值为黑色,TreeMap还会进行默认的调整,默认调整为红色

  • TreeMap集合的一些成员变量:
    在这里插入图片描述
    comparator:表示比较的规则
    root:记录红黑树根结点的地址值
    size:表示集合的长度,也表示红黑树中结点的个数

  • TreeMap的空参构造:
    在这里插入图片描述

  • TreeMap的带参构造:
    在这里插入图片描述
    就是创建TreeMap的时候要传递比较器对象,然后把传递过来的比较器对象传递给成员变量comparator

1.TreeMap中每一个节点的内部属性
K key;					//键
V value;				//值
Entry<K,V> left;		//左子节点
Entry<K,V> right;		//右子节点
Entry<K,V> parent;		//父节点
boolean color;			//节点的颜色2.TreeMap类中中要知道的一些成员变量
public class TreeMap<K,V>{//比较器对象private final Comparator<? super K> comparator;//根节点private transient Entry<K,V> root;//集合的长度private transient int size = 0;3.空参构造//空参构造就是没有传递比较器对象public TreeMap() {comparator = null;}4.带参构造//带参构造就是传递了比较器对象。public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}5.添加元素public V put(K key, V value) {return put(key, value, true);}
//put(key, value, true);
参数一:键
参数二:值
参数三:当键重复的时候,是否需要覆盖值true:覆盖false:不覆盖private V put(K key, V value, boolean replaceOld) {//获取根节点的地址值,赋值给局部变量tEntry<K,V> t = root;//判断根节点是否为null//如果为null,表示当前是第一次添加,会把当前要添加的元素,当做根节点//如果不为null,表示当前不是第一次添加,跳过这个判断继续执行下面的代码if (t == null) {//方法的底层,会创建一个Entry对象,把他当做根节点addEntryToEmptyMap(key, value);//表示此时没有覆盖任何的元素return null;}//表示两个元素的键比较之后的结果int cmp;//表示当前要添加节点的父节点Entry<K,V> parent;//表示当前的比较规则//如果我们是采取默认的自然排序,//那么此时comparator记录的是null,cpr记录的也是null//如果我们是采取比较器的排序方式,//那么此时comparator记录的是就是比较器Comparator<? super K> cpr = comparator;//表示判断当前是否有比较器对象//如果传递了比较器对象,就执行if里面的代码,此时以比较器的规则为准//如果没有传递比较器对象,就执行else里面的代码,此时以自然排序的规则为准if (cpr != null) {do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else {V oldValue = t.value;if (replaceOld || oldValue == null) {t.value = value;}return oldValue;}} while (t != null);} else {//把键进行强转,强转成Comparable类型的//要求:键必须要实现Comparable接口,如果没有实现这个接口//此时在强转的时候,就会报错。Comparable<? super K> k = (Comparable<? super K>) key;do {//把根节点当做当前节点的父节点parent = t;//调用compareTo方法,比较根节点和当前要添加节点的大小关系cmp = k.compareTo(t.key);if (cmp < 0)//如果比较的结果为负数//那么继续到根节点的左边去找t = t.left;else if (cmp > 0)//如果比较的结果为正数//那么继续到根节点的右边去找t = t.right;else {//如果比较的结果为0,会覆盖V oldValue = t.value;if (replaceOld || oldValue == null) {t.value = value;}return oldValue;}} while (t != null);}//就会把当前节点按照指定的规则进行添加addEntry(key, value, parent, cmp < 0);return null;}	private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {Entry<K,V> e = new Entry<>(key, value, parent);if (addToLeft)parent.left = e;elseparent.right = e;//添加完毕之后,需要按照红黑树的规则进行调整fixAfterInsertion(e);size++;modCount++;}private void fixAfterInsertion(Entry<K,V> x) {//因为红黑树的节点默认就是红色的x.color = RED;//按照红黑规则进行调整,可以看一下红黑规则的笔记//parentOf:获取x的父节点//parentOf(parentOf(x)):获取x的爷爷节点//leftOf:获取左子节点while (x != null && x != root && x.parent.color == RED) {//判断当前节点的父节点是爷爷节点的左子节点还是右子节点//目的:为了获取当前节点的叔叔节点if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//表示当前节点的父节点是爷爷节点的左子节点//那么下面就可以用rightOf获取到当前节点的叔叔节点Entry<K,V> y = rightOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {//叔叔节点为红色的处理方案//把父节点设置为黑色setColor(parentOf(x), BLACK);//把叔叔节点设置为黑色setColor(y, BLACK);//把爷爷节点设置为红色setColor(parentOf(parentOf(x)), RED);//把爷爷节点设置为当前节点x = parentOf(parentOf(x));} else {//叔叔节点为黑色的处理方案//表示判断当前节点是否为父节点的右子节点if (x == rightOf(parentOf(x))) {//表示当前节点是父节点的右子节点x = parentOf(x);//左旋rotateLeft(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateRight(parentOf(parentOf(x)));}} else {//表示当前节点的父节点是爷爷节点的右子节点//那么下面就可以用leftOf获取到当前节点的叔叔节点Entry<K,V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == leftOf(parentOf(x))) {x = parentOf(x);rotateRight(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateLeft(parentOf(parentOf(x)));}}}//把根节点设置为黑色root.color = BLACK;}

课堂思考问题:

  • TreeMap添加元素的时候,键是否需要重写hashCode和equals方法?

此时是不需要重写的,因为在TreeMap源码中添加元素时没有用到hashCOde和equals方法

HashMap是哈希表结构的,JDK8开始由数组,链表,红黑树组成的

  • 既然有红黑树,HashMap的键是否需要实现Compareable接口或者传递比较器对象呢?

不需要。 因为在HashMap的底层,默认是利用哈希值的大小关系来创建红黑树的

  • TreeMap和HashMap谁的效率更高?

如果是最坏情况,添加了8个元素,这8个元素形成了链表,此时TreeMap(底层红黑树)的效率要更高

但是这种情况出现的几率非常的少。 一般而言,还是HashMap(底层数组,链表,红黑树)的效率要更高

  • 你觉得在Map集合中,java会提供一个如果键重复了,不会覆盖的put方法呢?
    有,putIfAbsent这个方法就表示键重复时不会覆盖,与put相反
    此时putIfAbsent本身不重要。

传递一个思想:
代码中的逻辑都有两面性,如果我们只知道了其中的A面, 而且代码中还发现了有变量可以控制两面性的发生。
那么该逻辑一定会有B面。

习惯:

boolean类型的变量控制,一般只有AB两面,因为boolean只有两个值
int类型的变量控制,一般至少有三面,因为int可以取多个值

  • 三种双列集合,以后如何选择?
    HashMap LinkedHashMap TreeMap
    默认:HashMap(效率最高)
    如果要保证存取有序:LinkedHashMap
    如果要进行排序:TreeMap

1.6 可变参数 …

在这里插入图片描述
这种方式比较麻烦,因为每次都要创建一个数组储存,在放到创建的方法中调用

所以jdk5以后提出了一个特性,可变参数即方法形参的个数是可以发生变化的,可以是0,1,2,3……个
格式:属性类型...名字,例如:int...a

可变参数底层就是一个数组,java自动创建好的,所以,可变参数本质上就是一个数组

可变参数的细节:

  1. 在方法的形参中,最多只能写一个可变参数 错误的写法: public static int getSum(int...a,int...b)
  2. 在方法中,如果除了可变参数以外还有其他的形参,那么可变参数要写在最后 public static int getSum(int a,int b,int...number)

在这里插入图片描述

public class ArgsDemo1 {public static void main(String[] args) {int sum = getSum(1,2,3,4,5,6);System.out.println(sum);System.out.println(getSum(1,2,3));//6System.out.println(getSum(1,2,3,9,10,29));//54}//可变参数public static int getSum(int ...args){int sum =0;for (int i : args) {sum += i;}return sum;}
}

小结:
在这里插入图片描述

1.7 Collections

在这里插入图片描述
Collections 常用的API:
在这里插入图片描述
在运用Collections(集合的工具类)里面的方法时,基本上所有的方法都有静态标记
所以在调用其方法时就用类名.就行

例子:

import java.util.*;public class Main {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();//addAll,批量添加Collections.addAll(list,"abc","dce","123","456","qwer","zxvmn");System.out.println(list);//shuffle(),打乱Collections.shuffle(list);System.out.println(list);//sort排序,分两种,一种是默认排序,另外一种是指定方式排序//方式一:Collections.sort(list);System.out.println(list);//binarySearch,以二分查找法查找元素,注意顺序的才可以,倒序的反而不行int index = Collections.binarySearch(list, "456");System.out.println(index);//方式二:Collections.sort(list, new Comparator<String>() {//o1:表示当前要添加的元素//o2:表示已经在集合中存在的元素//返回值如果是负数,则会认为要添加的元素是小的,存在集合的左边//返回值如果是正数,则会认为要添加的元素是大的,存在集合的右边@Overridepublic int compare(String o1, String o2) {for (int i = 0; i < o2.length(); i++) {//此时为逆序排序if(o1.charAt(i)>o2.charAt(i)){return -1;}else{return 1;}}return 0;}});System.out.println(list);//copy,复制元素,//Collections.copy(list2,list1); 把list1中的元素拷贝到list2中//会覆盖原来的元素//注意:如果list1的长度大于list2的长度,就会报错!!!!!!ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1,"avc","w","p","o","s","y","t","48");Collections.copy(list1,list);System.out.println(list1);//fill,使用指定的数据填充集合//把集合中的所有数据都改成指定数据Collections.fill(list,"麻瓜");System.out.println(list);//max/min,获取最大/小值,通过ASCII码来判断System.out.println(Collections.max(list1));System.out.println(Collections.min(list1));//swap,交换集合中指定位置的元素Collections.swap(list1,0,5);System.out.println(list1);}
}

在这里插入图片描述

1.8 综合练习

一、自动点名器1

在这里插入图片描述

import java.util.*;public class Main {public static void main(String[] args) {/*班级里有N个学生,学生属性:姓名,年龄,性别实现随机点名器*///1.定义集合ArrayList<String> list = new ArrayList<>();//2.添加数据Collections.addAll(list,"麻瓜","张三","李四","范闲","范键","范统");//3.随机点名//法一://核心思想:如果是在集合里,那么就用随机函数限制在集合的长度里面,随机出来里面的索引//然后用集合的get(索引)就可以获取Random r =new Random();int index = r.nextInt(list.size());String name = list.get(index);System.out.println(name);//法二:Collections.shuffle(list);System.out.println(list.get(0));}
}

在这里插入图片描述

二、自动点名器2

在这里插入图片描述

那么要怎么去代码实现概率问题呢?

我们可以在集合当中去添加7个"1"和3个"0",其在整体中就占70%30%,然后打乱里面的数字,打乱完之后就从里面抽取0或1

如果抽到了1,那就是有70%的概率随机到了
如果抽到了0,那就是30%,这样就可以解决概率问题了

import java.util.*;public class Main {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,1,1,1,1,1,1,1);Collections.addAll(list,0,0,0);//打乱数字,使得数据获得随机性Collections.shuffle(list);System.out.println(list);Random r = new Random();int index = r.nextInt(list.size());int number = list.get(index);//已经获得随机概率的数字System.out.println(number);ArrayList<String> manList = new ArrayList<>();ArrayList<String> womanList = new ArrayList<>();Collections.addAll(manList,"m1","m2","m3","m4","m5","m6","m7","m8");Collections.addAll(womanList,"w1","w2","w3","w4");if(number ==1){//此判断是从manList里面抽取int manIndex = r.nextInt(manList.size());String name1 = manList.get(manIndex);System.out.println(name1);}else{//此判断是从womanList里面抽取int WomanIndex = r.nextInt(womanList.size());String name2 = womanList.get(WomanIndex);System.out.println(name2);}}
}

在这里插入图片描述

三、自动点名器3

IDEA快捷方式 shift + f6 批量改名
在这里插入图片描述
核心:被随机到了就不会再被随机到的做法是,每次随机到了这个索引,再删除就行了

import java.util.*;public class Main {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1,"m1","m2","m3","m4","m5","m6","m7","m8","w9","w10");//创建一个临时的集合,用来储存被点到同学的名字ArrayList<String> list2 = new ArrayList<>();//外循环:循环的轮数for (int j = 1; j <= 10; j++) {System.out.println("===========第"+j+"轮开始了=================");//获取集合的长度int list1length = list1.size();//随机点名Random r = new Random();//内循环表示随机抽取的过程for (int i = 0; i < list1length; i++) {//第一次随机的时候:i = 0 长度10//第二次随机的时候: i= 1 长度9//第三次随机的时候:i = 2 长度8//第四次随机的时候: i= 3 长度7//第五次随机的时候: i=4 长度6//第六次随机的时候: i=5 长度5  5<5 fales,循环终止int index = r.nextInt(list1.size());//选中删除String name = list1.remove(index);list2.add(name);System.out.println(name);}//此时表示一轮点名结束//此时list1空了,list2 10个学生的名字//我们就可以把list2里面的数据加到list1当中,再把list2清空,这样再一次外循环时就可以再次作为空集合进行存储了list1.addAll(list2);list2.clear();System.out.println(list1);System.out.println(list2);}}
}

在这里插入图片描述

四、自动点名器4

在这里插入图片描述

五、自动点名器5

在这里插入图片描述

六、Mao集合案例-省和市

在这里插入图片描述

import java.util.*;public class Main {public static void main(String[] args) {//1.创建Map集合HashMap<String,ArrayList<String>> hm = new HashMap<>();//2.创建单列集合存储市ArrayList<String> city1 = new ArrayList<>();city1.add("南京市");city1.add("扬州市");city1.add("苏州市");city1.add("无锡市");city1.add("常州市");ArrayList<String> city2 = new ArrayList<>();city2.add("武汉市");city2.add("孝感市");city2.add("十堰市");city2.add("宜昌市");city2.add("鄂州市");ArrayList<String> city3 = new ArrayList<>();city3.add("石家庄市");city3.add("唐山市");city3.add("邢台市");city3.add("保定市");city3.add("张家口市");//把省份和多个市添加到map集合hm.put("江苏省",city1);hm.put("湖北省",city2);hm.put("河北省",city3);/*江苏省=南京市,扬州市,苏州市,无锡市,常州市湖北省=武汉市,孝感市,十堰市,宜昌市,鄂州市河北省=石家庄市,唐山市,邢台市,保定市,张家口市*/Set<Map.Entry<String, ArrayList<String>>> entries = hm.entrySet();for (Map.Entry<String, ArrayList<String>> entry : entries) {//entry依次表示每一个键值对对象//然后将其对象里面的键和值分开String key = entry.getKey();ArrayList<String> value = entry.getValue();//然后到值的时候,利用StringJoinner的间隔将值分开StringJoiner sj = new StringJoiner(",",""," ");//第一个:间隔符号,第二个开始符号,第三个结束符号//最后遍历出来每个值,插入到sj中for (String city : value) {sj.add(city);}System.out.println(key+" = "+sj);}}
}

在这里插入图片描述

1.9 阶段项目

1.9.1 斗地主游戏1(控制台版)

在这里插入图片描述

PokerGame类:
import java.util.ArrayList;
import java.util.Collections;public class PokerGame {//牌盒//拼接花色图形+数字元素比如❤3,♠4static ArrayList<String> list = new ArrayList<>(); //这里加静态的原因是下方有静态代码块,静态只能访问静态//准备牌放在构造方法外面,为了不然每次都准备一次,这样就重复浪费了//静态代码块//特点:随着类的加载而加载的,而且只执行一次static {/*"♠,♣,❤,♦""3","4","5","6","7",,"8","9","10","3","Q","K","A","2"*/String[] color = {"♠","♣","❤","♦"};String[] number = {"3","4","5","6","7","8","9","10","3","Q","K","A","2"};for (String c : color) {// c 表示每一种花色for (String n : number) {//n 表示每一个数字//组合起来加到牌盒当中list.add(c+n);}}list.add("小王");list.add("大王");//准备牌工作完成}//所以在这里直接写空参构造方法就可以了public PokerGame(){//洗牌Collections.shuffle(list);System.out.println(list);//发牌的对象//先决定好发牌对象//斗地主发牌给三个人,但是最重要的是要有一个底牌ArrayList<String> lord = new ArrayList<>();ArrayList<String> player1 = new ArrayList<>();ArrayList<String> player2 = new ArrayList<>();ArrayList<String> player3 = new ArrayList<>();//然后给每个发牌对象,遍历得到牌盒里面的牌for (int i = 0; i < list.size(); i++) {//i:就表示牌集合里面的索引//把索引拿出来String poker = list.get(i);//在斗地主的过程当中,前3张牌要留给底牌//所以索引0-2要留下if(i<=2){lord.add(poker);continue;}//給三民玩家轮流发牌//采用取模来分配,为什么呢?//因为“%3”在运算的到时候有三种余数,可以用来三等分//用索引i%3,如果等于0就发给玩家1//如果等于1,发给玩家2;如果为2,就发给玩家3if(i%3==0){player1.add(poker);}else if(i%3==1){player2.add(poker);}else{player3.add(poker);}}//看牌//怎么看?在外面定义一个方法然后调用//看牌无非就是遍历集合lookPoker("底牌",lord);lookPoker("magua",player1);lookPoker("zhangsan",player2);lookPoker("lisi",player3);}//那么要定义什么类型的方法呢,不返回值//形参为斗地主玩家的名字和牌public void lookPoker(String name,ArrayList<String> list){/*参数一:玩家的名字参数二:每位玩家的牌*/System.out.print(name+":");for (String poker : list) {System.out.print(poker+" ");}System.out.println();}
}APP测试类:
public class App {/*完成控制台版的三步骤:准备牌洗牌发牌"♠,♣,❤,♦""3","4","5","6","7",,"8","9","10","3","Q","K","A","2"*/public static void main(String[] args) {new PokerGame();//创建对象实际上会调用里面的空参构造}}

在这里插入图片描述

1.9.2 斗地主游戏2 第一种排序方式

在这里插入图片描述
以十二张牌为例子,牌是字符串,它里面的花色数字组合里面斗地主的规则谁大谁小该怎么判定呢?
在这里插入图片描述
所以我们可以按照牌指定的规则,进行手动的排列,再跟12345678910这样的数字一一对应,那么对应起来之后,数字越大,所对应的牌也就越大

在这里插入图片描述

比如:
怎么认为大王是最大的呢,其实就是通过下面的序号来判断的。
大王是序号数字12,红心2是序号数字10

在这里插入图片描述

所以,先找一个map集合把这种对应关系去存起来。那么再map集合当中,序号就作为键,上面牌花色数字组合就作为值

与此同时,创建一个单列集合,用来单独地存储牌的序号,那么这样准备牌的动作才算是做好了

  • 到洗牌了,其实打乱序号就行了,那么发牌发的也就是序号。前面牌盒发的三个序号作为底牌,其余序号就依次发给三个玩家
  • 发完之后序号是没有顺序的,所以要进行排序,完成后在一开始的map集合当中去找对应序号的牌就可以了

在这里插入图片描述

在这里插入图片描述

PokerGame2类:
import java.util.*;public class PokerGame2 {//牌盒 Map//此时只有把牌跟序号产生对应关系就可以了,不需要按照序号进行排序//所以只有HashMao就可以了static HashMap<Integer,String> hm = new HashMap<>();//用于对应序号与牌面static ArrayList<Integer> list = new ArrayList<>();//用于添加序号//TreeMap按键的方式从小到大存储//◆3 ◆4  ◆5 ◆6 ◆7//1   2    3   4    5//HashMap 这个侧重点注重排和数字之间的对应关系//◆3 ◆5  ◆4 ◆7 ◆6//1   3    2    5   4//如果用TreeMap进行存储 玩家一:145//◆3 ◆6 ◆7//如果用HashMap进行存储static {String[] color = {"♦","♣","❤","♠"};String[] number = {"3","4","5","6","7","8","9","10","3","Q","K","A","2"};//用来定义牌的序号int serialNumber = 1;/*{1=♠3, 2=♠4, 3=♠5, 4=♠6, 5=♠7, 6=♠8, 7=♠9, 8=♠10, 9=♠3, 10=♠Q, 11=♠K, 12=♠A, 13=♠2, 14=♣3,15=♣4, 16=♣5, 17=♣6, 18=♣7, 19=♣8, 20=♣9, 21=♣10, 22=♣3, 23=♣Q, 24=♣K, 25=♣A, 26=♣2, 27=❤3,28=❤4, 29=❤5, 30=❤6, 31=❤7, 32=❤8, 33=❤9, 34=❤10, 35=❤3, 36=❤Q, 37=❤K, 38=❤A, 39=❤2,40=♦3, 41=♦4, 42=♦5, 43=♦6, 44=♦7, 45=♦8, 46=♦9, 47=♦10, 48=♦3, 49=♦Q, 50=♦K, 51=♦A,52=♦2, 53=小王, 54=大王}但是这样外循环遍历花色,内循环遍历数字,花色数字排序不是按顺序的,只按花色来排玩家:1 2 3 4 5 14♠3 ♠4 ♠5 ♠6 ♠7 ♣3我们想要的排列是:♠3 ♣3 ♠4 ♠5 ♠6 ♠7把外循环改成数字开始,内循环改成花色开始就oK*/for (String n : number) {// c 表示每一种花色for (String c : color) {//n 表示每一个数字//组合起来加到牌盒当中hm.put(serialNumber,c+n);list.add(serialNumber);serialNumber++;}}hm.put(serialNumber,"小王");list.add(serialNumber);serialNumber++;hm.put(serialNumber,"大王");list.add(serialNumber);//因为大王是最后一张牌了,所以大王在添加的时候序号就可以不用变化了System.out.println(hm);System.out.println(list);}public PokerGame2(){//洗牌Collections.shuffle(list);//发牌//这里才用TreeSet是因为它默认进行数字排序//如果用的是ArrayList添加到里面的数据是乱的,又要多一步进行排序TreeSet<Integer> lord = new TreeSet<>();TreeSet<Integer> player1 = new TreeSet<>();TreeSet<Integer> player2 = new TreeSet<>();TreeSet<Integer> player3 = new TreeSet<>();for (int i = 0; i < list.size(); i++) {//i:依次表示集合中的每一个索引//list.get(i)元素:牌的序号int serialNumber = list.get(i);if(i<=2){lord.add(serialNumber);continue;}if(i%3 ==0){player1.add(serialNumber);}else if(i%3==1){player2.add(serialNumber);}else{player3.add(serialNumber);}}//看牌lookPoker("底牌",lord);lookPoker("magua",player1);lookPoker("zhangsan",player2);lookPoker("lisi",player3);}/*参数一:玩家的名字参数二:牌的序号*/public void lookPoker(String name,TreeSet<Integer> ts){System.out.println(name+":");//遍历TreeSet单列集合得到每一个序号,再拿着序号到map集合中去找真正的牌for (Integer serialNumber : ts) {String poker = hm.get(serialNumber);System.out.print(poker+":");}System.out.println();}}APP2类:
public class APP2 {public static void main(String[] args) {new PokerGame2();}
}

在这里插入图片描述

1.9.3 斗地主游戏3 第二种排序方式

下面的价值就是人为规定的,有了价值之后,就按照价值进行排序
在这里插入图片描述
在这里插入图片描述

PokerGame3类:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;public class PokerGame3 {//牌盒//拼接花色图形+数字元素比如❤3,♠4static ArrayList<String> list = new ArrayList<>(); //这里加静态的原因是下方有静态代码块,静态只能访问静态static HashMap<String,Integer> hm = new HashMap<>();//静态代码块//特点:随着类的加载而加载的,而且只执行一次static {/*"♠,♣,❤,♦""3","4","5","6","7",,"8","9","10","3","Q","K","A","2"*/String[] color = {"♦","♣","❤","♠"};String[] number = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};for (String c : color) {// c 表示每一种花色for (String n : number) {//n 表示每一个数字//组合起来加到牌盒当中list.add(c+n);}}list.add(" 小王");list.add(" 大王");//准备牌工作完成//指定牌的价值//牌上的数字到map集合中判断是否存在//存在,获取价值//不存在,本身的数字就是价值hm.put("J",11);hm.put("Q",12);hm.put("K",13);hm.put("A",14);hm.put("2",15);hm.put("小王",50);hm.put("大王",100);}//所以在这里直接写空参构造方法就可以了public PokerGame3(){//洗牌Collections.shuffle(list);System.out.println(list);//发牌的对象//先决定好发牌对象//斗地主发牌给三个人,但是最重要的是要有一个底牌ArrayList<String> lord = new ArrayList<>();ArrayList<String> player1 = new ArrayList<>();ArrayList<String> player2 = new ArrayList<>();ArrayList<String> player3 = new ArrayList<>();//然后给每个发牌对象,遍历得到牌盒里面的牌for (int i = 0; i < list.size(); i++) {//i:就表示牌集合里面的索引//把索引拿出来String poker = list.get(i);//在斗地主的过程当中,前3张牌要留给底牌//所以索引0-2要留下if(i<=2){lord.add(poker);continue;}//給三民玩家轮流发牌//采用取模来分配,为什么呢?//因为“%3”在运算的到时候有三种余数,可以用来三等分//用索引i%3,如果等于0就发给玩家1//如果等于1,发给玩家2;如果为2,就发给玩家3if(i%3==0){player1.add(poker);}else if(i%3==1){player2.add(poker);}else{player3.add(poker);}}//排序order(lord);order(player1);order(player2);order(player3);//看牌//怎么看?在外面定义一个方法然后调用//看牌无非就是遍历集合lookPoker("底牌",lord);lookPoker("magua",player1);lookPoker("zhangsan",player2);lookPoker("lisi",player3);}//那么要定义什么类型的方法呢,不返回值//形参为斗地主玩家的名字和牌public void lookPoker(String name,ArrayList<String> list){/*参数一:玩家的名字参数二:每位玩家的牌*/System.out.print(name+":");for (String poker : list) {System.out.print(poker+" ");}System.out.println();}//利用牌的价值进行排序//参数:集合//比如:♥5 ♥3 ♥6 ♥7 ♥9//当插入排序时,先把前面的第一张牌认为是有序序列,后面的所以认为是无序序列//然后遍历无序序列的每一张牌,把其插入到有序序列当中public void order(ArrayList<String> list){Collections.sort(list, new Comparator<String>() {//Array.sort(插入排序+二分查找)@Overridepublic int compare(String o1, String o2) {//o1:表示当前要插入到有序序列中的牌,比如♥3//o2:表示在有序序列中已经存在的牌。比如♥5//负数:o1小,那么就插入到前面//整数:o1大,插入到后面去//如果0,o1的数字与o2的是一样的,需要按照花色再次进行排序//1.计算o1的花色和价值 ♥3String color1 = o1.substring(0, 1);//那么价值怎么计算呢,除了要计算o1的价值,还要计算o2的价值int value1 = getValue(o1);//2.计算o2的花色和价值String color2 = o2.substring(0, 1);int value2 = getValue(o2);//3.笔记o1和o2的价值int i  = value1 - value2;return i==0?color1.compareTo(color2):i;}});}//计算牌的价值//参数:牌//返回值:价值public int getValue(String poker){//♥3 小王//获取牌上的数字String number = poker.substring(1);//把这里截取出来的结果,让这个结果再map集合中存在 “ 大王”//空格截取不到,只能截取到大王,那么大王到里面的集合当中判断集合就存在了//拿着数字到map集合中判断是否存在if(hm.containsKey(number)){//如果在这里大小王存在,就不会走到下方的类型转化中去//存在,获取价值return hm.get(number);}else{//不存在,类型转换//但是如果大王小王在这里的话,汉字是无法转化为数字的//往上面推return Integer.parseInt(number);}}
}APP3类:
public class App3 {public static void main(String[] args) {new PokerGame3();}
}

1.10 不可变集合

不可变集合:

指的是不可以被修改的集合,比如不可以修改长度、不可以修改内容等

在这里插入图片描述
创建不可变集合的书写格式:
在这里插入图片描述
例子:

import java.lang.invoke.CallSite;
import java.util.*;public class Main {public static void main(String[] args) {//创建不可变的list集合List<String> list = List.of("麻瓜","小老板","愣头青","铁头娃");System.out.println(list.get(0));System.out.println(list.get(1));System.out.println(list.get(2));System.out.println(list.get(3));System.out.println("============================");//增强for循环遍历for (String s : list) {System.out.println(s);}System.out.println("============================");//迭代器遍历Iterator<String> it = list.iterator();while(it.hasNext()){String s = it.next();System.out.println(s);}System.out.println("============================");//普通for遍历for (int i = 0; i < list.size(); i++) {String s = list.get(i);System.out.println(s);}System.out.println("---------------------------------------");//不可变集合一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作//list.remove("麻瓜")报错//list.add("aaa"); 报错//list.set(0,"aaa"); 报错//创建不可变的set集合//细节:set集合里面的元素是唯一的,一定要保证其唯一性Set<String> set = Set.of("magua","zhangsan","lisi");for (String s : set) {System.out.println(s);}System.out.println("---------------------------------------");//创建不可变的map集合//细节:键是必须唯一的,不能重复//      Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对Map<String,String> map = Map.of("张三","guangdoor","李四","新日暮里","魔男","幻想乡");//keyset获取键Set<String> keys = map.keySet();for (String key : keys) {String value = map.get(key);System.out.println(key+"="+value);}System.out.println("==========================");//entrySet获取整体Set<Map.Entry<String, String>> entries = map.entrySet();for (Map.Entry<String, String> entry : entries) {String key = entry.getKey();String value = entry.getValue();System.out.println(key+"="+value);}System.out.println("==========================");}
}

提问:创建一个能接收多个键和值的方法
当类型不确定时:用泛型方法,比如键为K和值为V

创建Map的不可变集合
细节1:

  • 键是不能重复的

细节2:

  • Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对

在这里插入图片描述
报错原因:

如果一个形参里有可变参数,那么形参就必须要写到最后。但是一个键形参里面又不能有多个可变参数,只能有一个,所以这两个可变参数是不能共存的

解决方案:
把键和值看做成一个整体,然后在传递给of方法就行了

法一:

import java.lang.invoke.CallSite;
import java.util.*;public class Main {public static void main(String[] args) {//1.创建一个普通的map集合HashMap<String,String> hm = new HashMap<>();hm.put("张三","南京");hm.put("李四","北京");hm.put("王五","上海");hm.put("赵六","北京");hm.put("孙七","深圳");hm.put("周八","杭州");hm.put("吴九","宁波");hm.put("郑十","苏州");hm.put("刘一","无锡");hm.put("陈二","嘉兴");hm.put( "aaa", "111");//2.利用上面的数据来获取一个不可变的集合//获取到所有的键值对对象(entry对象)Set<Map.Entry<String, String>> entries = hm.entrySet();//把entries变成一个数组//Map.Entry[] arr = entries.toArray(new Map.Entry[0]);Map.Entry[] arr1 = new Map.Entry[0];//给它个数组Map.Entry[] arr2 = entries.toArray(arr1);//将集合中的所有数据放在数组当中//toArray方法在底层会比较集合的长度跟数组的长度两者的大小//如果集合的长度 > 数组的长度:数据在数组中放不下,此时会根据实际数据的个数,重新创建数组//如果集合的长度 <=数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用//不可变的map集合Map map = Map.ofEntries(arr2);//ofEntries方法里面的形参就是可变参数,可变参数里头就是一个数组,那么传递过去就是可以的}
}

法二:链式简化

Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));

法三:不可变map集合简化

Map<String, String> map = Map.copyOf(hm);

小结:
在这里插入图片描述

2. Stream流

案例:
在这里插入图片描述
不用stream流的麻烦操作:

import java.util.ArrayList;public class Main {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();list1.add("麻瓜");list1.add("捞得一");list1.add("小老板");list1.add("小青年");list1.add("厨师");//1.把所有以“小”开头的元素再存储到新集合中ArrayList<String> list2 = new ArrayList<>();for (String name : list1) {if(name.startsWith("小")){list2.add(name);}}System.out.println(list2);//2.把“张”开头的,长度为3的元素再次存储到新集合中ArrayList<String> list3 = new ArrayList<>();for (String name : list2) {if(name.length()==3){list3.add(name);}}System.out.println(list3);}
}

在这里插入图片描述
使用stream流:
一行代码直接搞定

        list1.stream().filter(name->name.startsWith("小")).filter(name->name.length()==3).forEach(name-> System.out.println(name));

在这里插入图片描述

2.1 初识stream流

什么是流:
理解成工厂的流水线
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

stream流的作用:结合了lambda表达式,简化集合、数组的操作

在这里插入图片描述

Stream流的使用步骤
双列集合无法直接使用stream流,要使用keySet或者entrySet转化为单列集合才能使用
在这里插入图片描述
例子:
1.单列集合获取stream流

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.Stream;public class Main {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"a","b","c","d");/*//获取到一条流水线,并把集合中的数据放到流水线上Stream<String> stream1 = list.stream();//使用终结方法打印一下流水线上的所有数据stream1.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {//s:依次表示流水线上的每一个数据System.out.println(s);}}); */list.stream().forEach(s-> System.out.println(s));}
}

在这里插入图片描述
2.双列集合获取stream流

public class Main {public static void main(String[] args) {HashMap<String,Integer> hm = new HashMap<>();hm.put("aaaa",111);hm.put("bbbb",222);hm.put("cccc",333);hm.put("dddd",444);//第一种keySet获取stream流hm.keySet().stream().forEach(s-> System.out.println(s));//第二种entrySet获取stream流System.out.println(":==============");hm.entrySet().stream().forEach(s-> System.out.println(s));}
}

在这里插入图片描述
3.数组获取stream流

public class Main {public static void main(String[] args) {int[] arr1 = {1,2,3,4,5,6,7,8,9};String[] arr2 = {"a","b","c"};Arrays.stream(arr1).forEach(s-> System.out.println(s));System.out.println("========================");Arrays.stream(arr2).forEach(s-> System.out.print(s));}
}

在这里插入图片描述
4.一堆的零散数据获取stream流
前提条件:必须要是同种数据类型的

    public static void main(String[] args) {Stream.of(1,2,3,4,5).forEach(s-> System.out.println(s));Stream.of("a","b","c","d").forEach(s-> System.out.print(s));}

在这里插入图片描述

注意:
在这里插入图片描述

2.2 Steam流的中间方法

在这里插入图片描述

  • filter 过滤
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;public class Main {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"麻瓜","张三","小老板","小青年","愣头青","小三");//法一list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {//如果返回值为true,表示当前数据留下//如果返回值为false,表示当前数据舍弃不变return s.startsWith("小");}}).forEach(s-> System.out.println(s));System.out.println("===========================");//法二:链式编程list.stream().filter(s->s.startsWith("小")).filter(s->s.length()==3).forEach(s-> System.out.println(s));System.out.println(list);//经过stream流过滤之后,原本的list并没有受到影响}
}

在这里插入图片描述

  • limit 获取前几个元素
public class Main {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"麻瓜","张三","小老板","小青年","愣头青","小三");list.stream().limit(3).forEach(s-> System.out.println(s));}
}

在这里插入图片描述

  • skip 跳过前几个元素
        list.stream().skip(4).forEach(s-> System.out.println(s));

在这里插入图片描述

  • distinct 元素去重(底层依赖hashCode和equals方法)
    public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"麻瓜","张三","小老板","小青年","愣头青","小三","小老板","小老板");list.stream().distinct().forEach(s-> System.out.println(s));}

在这里插入图片描述

  • concat 合并a和b两个流为一个流
    public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1,"麻瓜","张三","小老板","小青年","愣头青","小三","小老板","小老板");ArrayList<String> list2 = new ArrayList<>();Collections.addAll(list2,"guangdoor","van");Stream.concat(list1.stream(),list2.stream()).forEach(s-> System.out.println(s));}

在这里插入图片描述

  • map 转换流中的数据类型
public class Main {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1,"麻瓜-1","张三-2","小老板-3","小青年-4","愣头青-5","小三-6");//需求:只获取里面的数字并进行打印//String-> int//第一个类型:流中原本的数据类型//第二个数据类型:要转成之后的类型//apply的形参s:依次表示流里面的每一个数据//返回值:表示转换之后的数据//当map方法执行完毕之后,流上的数据就变成了整数//所以在下面forEach当中,s依次表示流里面的每一个数据,这个数据现在就是整数了list1.stream().map(new Function<String, Integer>() {@Overridepublic Integer apply(String s){String[] arr = s.split("-");//把-分开后,剩下名字和数字//所以,arr[0]表示前面的名字,arr[1]表示后面的数字String ageString = arr[1];int age = Integer.parseInt(ageString);return age;}}).forEach(s-> System.out.println(s));System.out.println("===================");//法二:list1.stream().map(s->Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));}
}

在这里插入图片描述

2.3 Steam流的终结方法

在这里插入图片描述

2.3.1 forEach 遍历

    public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1,"麻瓜","张三","小老板","小青年","愣头青","小三");//voidforEach(Consumer action)   遍历//法一://Consumer的泛型:表示流中数据的类型//accept方法的形参s:依次表示流里面的每一个数据//方法体:对每一个数据的处理操作(打印)list1.stream().forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});System.out.println("=================================");//法二:list1.stream().forEach(s-> System.out.println(s));}

在这里插入图片描述

2.3.2 count 统计

        //long count()     统计long count = list1.stream().count();System.out.println(count);

在这里插入图片描述

2.3.3 toArray 收集流数据放进数组

        //toArray有两个,一个空参的收集Object类型,一个具体类型的Object[] arr1 = list1.stream().toArray();System.out.println(Arrays.toString(arr1));//toArray方法的完整解析://IntFunction的泛型:具体类型的数组//apply的形参:流中的数据个数,要跟数组的长度保持一致//apply的返回值:具体类型的数组//方法体:就是创建数组//toArray方法的参数的作用:负责创建一个指定类型的数组//toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组当中//toArray方法的返回值:是一个装着流里面所有数据的数组String[] arr = list1.stream().toArray(new IntFunction<String[]>() {@Overridepublic String[] apply(int value) {return new String[value];}});System.out.println(Arrays.toString(arr));

在这里插入图片描述

2.3.4 collect 收集流数据放进集合

collect(Collector collector) 收集流中的数据,放到集合中(List Set Map)

注意点:

如果要收集到Map集合当中,键不能重复,否则会报错

public class Main {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1,"a-男-15","t-男-15","b-女-14","c-女-16","d-男-17","e-男-18","f-女-13","g-男-19");//收集List集合当中//需求://把所有的男性收集起来List<String> newList1 = list1.stream().filter(s->"男".equals(s.split("-")[1])).collect(Collectors.toList());System.out.println(newList1);System.out.println("================================");//收集Set集合当中//需求://同上//set集合中元素不会重复Set<String> newList2 = list1.stream().filter(s->"男".equals(s.split("-")[1]))/*toMap:参数一表示键的生成规则参数二表示值的生成规则参数一:也就是collect里面上面那一堆Function泛型一:表示流中每一个数据的类型泛型二:表示Map集合中键的数据类型方法apply形参:依次表示流里面的每一个数据方法体:生成键的代码返回值:已经生成的键参数二:Function泛型一:表示流中每一个数据的类型泛型二:表示Map集合中键的数据类型方法apply形参:依次表示流里面的每一个数据方法体:生成键的代码返回值:已经生成的值*/.collect(Collectors.toSet());System.out.println(newList2);System.out.println("===================================");//收集Map集合当中//谁作为键,谁作为值//把所有的男性收集起来//键:姓名  值:年龄Map<Object, Integer> map = list1.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toMap(new Function<String, Object>() {@Overridepublic String apply(String s) {//a-男-15return s.split("-")[0];}},new Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.parseInt(s.split("-")[2]);}}));System.out.println(map);//其lambda表达式:Map<String, Integer> map2 = list1.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toMap(s -> s.split("-")[0],s -> Integer.parseInt(s.split("-")[2])));System.out.println(map2);}
}

在这里插入图片描述
小结:

在这里插入图片描述

2.3.5 apply方法

apply的形参:

流中的数据个数,要跟数组的长度保持一致

apply的返回值:

具体类型的数组

2.4 综合练习

2.4.1 数据过滤

在这里插入图片描述

    public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list,1,2,3,4,5,6,7,8,9);//进行判断,如果是偶数,则返回true,true就保留//将结果保存起来,就是收集的意思,一般都是收集到集合当中List<Integer> newlist = list.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());System.out.println(newlist);}

在这里插入图片描述

2.4.2 字符串过滤并收集

在这里插入图片描述

public class Main {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("zhangsan,23");list.add("lisi,24");list.add("wangwu,25");//保留年龄大于等于24的人//要分割字符串里面的逗号s.split(","),分割后取索引[1]//把取到到字符串数字转换为整型,然后判断>=24,把符合条件的集合过滤出来//原式表达list.stream().filter(s->Integer.parseInt(s.split(",")[1])>=24).collect(Collectors.toMap(new Function<String, Object>() {@Overridepublic String apply(String s){return s.split(",")[0];}}, new Function<String, Integer>() {@Overridepublic Integer apply(String s){return Integer.parseInt(s.split(",")[1]);}}));//lambda表达式Map<String, Integer> newlist = list.stream().filter(s -> Integer.parseInt(s.split(",")[1]) >= 24).collect(Collectors.toMap(s -> s.split(",")[0],s -> Integer.parseInt(s.split(",")[1])));System.out.println(newlist);}
}

在这里插入图片描述

2.4.3 自定义对象过滤并收集

在这里插入图片描述

Actor类:创建省略测试类
public class Test {public static void main(String[] args) {//1.创建两个ArrayList集合ArrayList<String> manList = new ArrayList<>();ArrayList<String> womanList = new ArrayList<>();//2.添加数据Collections.addAll(manList,"小黑子,24","叶剃咸,23","刘不甜,22","吴签,24","谷嘉,30","肖梁梁,27");Collections.addAll(womanList,"赵小颖,35","杨颖,36","高元元,43","张天天,31","刘诗,35","杨小幂,33");//3.男演员只要3个字的前两人Stream<String> stream1 = manList.stream().filter(s -> s.split(",")[0].length() >= 3).limit(2);//4.女演员只要姓杨的,并且不要第一个Stream<String> stream2 = womanList.stream().filter(s -> s.split(",")[0].startsWith("杨")).skip(1);//5.把过滤后的男演员姓名和女演员的姓名合并到一起//演员信息封装成Actor对象 String->Actor对象(类型转换)
//        Stream.concat(stream1,stream2)
//                .map(new Function<String, Actor>() {
//                    @Override
//                    public Actor apply(String s){
//                        //"小黑子,24"
//                        String name = s.split(",")[0];
//                        int age = Integer.parseInt(s.split(",")[1]);
//                        return new Actor(name,age);
//                    }
//                }).forEach(s-> System.out.println(s));//lambda表达式List<Actor> list = Stream.concat(stream1, stream2).map(s -> new Actor(s.split(",")[0],Integer.parseInt(s.split(",")[1]))).collect(Collectors.toList());//把数据存放到数组当中System.out.println(list);}
}

在这里插入图片描述

3.方法引用

在这里插入图片描述
在这里插入图片描述

方法引用

  1. 引用处需要是函数式接口
  2. 被引用的方法需要已经存在
  3. 被引用方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
  4. 被引用方法的功能需要满足当前的要求
public class Main {public static void main(String[] args) {//需求:创建一个数组,进行倒序排序Integer[] arr = {3,5,4,1,6,2};//匿名内部类Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2-o1;}});//lambda表达式//因为第二个参数的类型Comparator是一个函数式接口Arrays.sort(arr,(Integer o1,Integer o2)->{return o2 - o1;});//lambda表达式简化版Arrays.sort(arr,(o1,o2)->o2-o1);//表示引用FuctionDemo1类里面的subtraction方法//把这个方法当做抽象方法的方法体Arrays.sort(arr,Main::subtraction);System.out.println(Arrays.toString(arr));}//可以是Java已经写好的,也可以是一些第三方的工具类public static int subtraction(int num1,int num2){return num2 - num1;}
}

在这里插入图片描述
小结:
在这里插入图片描述

3.1 引用静态方法

在这里插入图片描述
引用静态方法
格式:类名::静态方法
范例:Integer::parseInt

静态方法中没有this!!
在这里插入图片描述
在这里插入图片描述

public class Main {public static void main(String[] args) {//1.创建ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"1","2","3","4","5");//转换成int类型list.stream().map(new Function<String, Integer>() {@Overridepublic Integer apply(String s){int i = Integer.parseInt(s);return i;}}).forEach(s-> System.out.println(s));System.out.println("==================================");//此时上面这样的function方法我不想写,用别人相同的代码拿过来//1.方法需要已经存在//2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致//3.方法的功能需要把形参的字符串转换成整数//那么就是parseInt了list.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));}
}

在这里插入图片描述

3.2 引用其他类的成员方法

在这里插入图片描述
在这里插入图片描述

public class Main {public static void main(String[] args) {//1.创建ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");//3.过滤数据(只要以张开头,而且名字是3个字的)list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3);//匿名类写法list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {return s.startsWith("张")&&s.length()==3;}}).forEach(s-> System.out.println(s));System.out.println("====================");//静态方法中没有this,故要重新在本类new一个本类的对象出来引用list.stream().filter(new Main()::stringJudge).forEach(s-> System.out.println(s));}public boolean stringJudge(String s){return s.startsWith("张")&&s.length()==3;}
}

在这里插入图片描述

3.3 引用本类和父类的成员方法

在这里插入图片描述

3.4 引用构造方法

在这里插入图片描述
目的:为了创建对象

练习:
在这里插入图片描述
在这里插入图片描述

Student类:public class Student {private String name;private int age;public Student() {}public Student(String str){//这里str不是代表name,而是依次表示流里面的每一个数据String[] arr = str.split(",");this.name = arr[0];this.age = Integer.parseInt(arr[1]);}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}测试类:public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"麻瓜,15","小老板,34","石乐志,18");//封装成Student对象并收集到List集合中//String -> Student//方法引用List<Student> list2 = list.stream().map(Student::new).collect(Collectors.toList());System.out.println(list2);}

在这里插入图片描述

3.5 类名引用构造方法

在这里插入图片描述
该方法引用的规则:

  1. 需要有函数式接口
  2. 被引用的方法必须已经存在
  3. 被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致
  4. 被引用的功能需要满足当下的需求

抽象方法形参的详解:
在这里插入图片描述
其局限性:

  1. 不能引用所有类中的成员方法
  2. 是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么久只能引用这个类中的方法

练习:
在这里插入图片描述

public class FunctionDemo2 {public static void main(String[] args) {//1.创建集合对象ArrayList<String> list = new ArrayList<>();//2.添加数据Collections.addAll(list,"aaa","bbb","ccc","ddd");//3.变成大写后进行输出//String-> Stringlist.stream().map(new Function<String, String>() {@Overridepublic String apply(String s){return s.toUpperCase();}}).forEach(s-> System.out.println(s));System.out.println("-------------------------------");//类名引用方法简化list.stream().map(String::toUpperCase).forEach(s-> System.out.println(s));}
}

在这里插入图片描述

在这里插入图片描述

  • 符合规则3且跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法是无参的成员方法
    在这里插入图片描述

  • 类名不是什么方法都可以引用的,有规定,主要看第一个参数
    被引用方法的调用者,决定了可以引用哪些类中的方法,故已经表明使用String类型的
    在这里插入图片描述

3.6 引用数组的构造方法

在这里插入图片描述
细节:创建数组的类型时,需要跟流中数据的类型保持一致

练习:
在这里插入图片描述

 public class FunctionDemo2 {public static void main(String[] args) {//1.创建集合对象ArrayList<Integer> list = new ArrayList<>();//2.添加数据Collections.addAll(list,1,2,3,4,5);//收集到数组当中//匿名内部类Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {@Overridepublic Integer[] apply(int value) {return new Integer[value];}});System.out.println(Arrays.toString(arr));//引用数组的构造方法Object[] arr2 = list.stream().toArray(Integer[]::new);System.out.println(Arrays.toString(arr2));}
}

在这里插入图片描述
小结:
在这里插入图片描述
在这里插入图片描述

3.7 方法引用练习

3.7.1 练习1

在这里插入图片描述

student类:
public class Student {private String name;private int age;public Student() {}public Student(String str){//这里str不是代表name,而是依次表示流里面的每一个数据String[] arr = str.split(",");this.name = arr[0];this.age = Integer.parseInt(arr[1]);}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}测试类:public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"张无忌,15","周芷若,14","赵敏,13","张强,20","张三丰,100","张翠山,40","张良,35","王二麻子,37","谢广坤,41");//先把字符串变成Student对象,然后再把Student对象收集起来Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);//一开始的时候,流里面的数据一定是字符串,当map方法执行完毕时,已经把字符串对象变为了Student对象//所以map后面toArray收集时,就不能创建String对象了,要对应Student对象System.out.println(Arrays.toString(arr));}

在这里插入图片描述

3.7.2 练习2

方法细节:

  1. 现在有没有一个方法符合当前的需求
  2. 如果有这样的方法,这个方法是否满足引用的规则
    静态 类名::方法名
    成员方法 对象名::方法名(本类this,父类super)
    构造方法 类名::new

在这里插入图片描述

  public static void main(String[] args) {ArrayList<Student> list = new ArrayList<>();list.add(new Student("zhangsan",23));list.add(new Student("magua",24));list.add(new Student("lisi",25));//获取姓名并放到数组当中//刚开始的时候,想不到引用什么方法,那么就可以时候匿名内部类String[] namelist = list.stream().map(new Function<Student, Object>() {@Overridepublic String apply(Student student) {return student.getName();}}).toArray(String[]::new);System.out.println(Arrays.toString(namelist));//方法引用简化String[] arr = list.stream().map(Student::getName).toArray(String[]::new);System.out.println(Arrays.toString(arr));}

在这里插入图片描述

3.7.3 练习3

在这里插入图片描述

import java.util.ArrayList;
import java.util.List;public class Main {public static void main(String[] args) {List<Student> students = new ArrayList<>();// 添加学生对象students.add(new Student("张三", 23));students.add(new Student("李四", 24));// 将每个学生对象的姓名和年龄拼接成字符串,并放到数组当中,使用方法引用完成String[] namesAndAges = students.stream().map(Student::getNameAndAge).toArray(String[]::new);// 输出结果for (String nameAndAge : namesAndAges) {System.out.println(nameAndAge);}}
}class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public String getNameAndAge() {return name + "-" + age;}
}

在这里插入图片描述

4.异常

异常:

异常就是代表程序出现的问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.error:硬件类的问题

2.Exception:叫做异常,代表程序可能出现的问题。
我们通常会用Exception以及他的子类来封装程序出现的问题。

运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒。
运行时出现的异常(如:数组索引越界异常)

1)编译时异常:没有继承RuntimeExcpetion的异常,直接继承于Excpetion。
编译阶段就会错误提示
2)运行时异常:RuntimeException本身和子类。
3)编译阶段没有错误提示,运行时出现的

在这里插入图片描述
小结:

在这里插入图片描述

4.1 编译时和运行时异常

String name = "2030年1月1日";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
Date date = sdf.parse(time);
System.out.println(date);

parse正常情况下会报错,需要在main函数的后面加上throws ParseException才能将报错取消
这就是编译时异常,在编译阶段,必须要手动处理,否则代码报错

int[] arr = {1,2,3,4,5};
System.out.println(arr[10]);

数组越界异常,标准的运行时异常,在编译阶段是不需要处理的,是代码运行时出现的异常

在这里插入图片描述
为什么不把所有的异常归为一类呢?
在编译阶段,java不会运行diamagnetic,只会检查语法是否错误,或者做一些性能的优化,更多的是提醒程序员检查本地信息

运行时异常就是代码出错而导致程序出现问题
在这里插入图片描述
小结:

在这里插入图片描述

4.2 异常在代码中的两个作用

在这里插入图片描述
异常:空指针异常

student类:
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}测试类:public static void main(String[] args) {Student[] arr = new Student[3];//null null nullString name = arr[0].getName();System.out.println(name);}

在这里插入图片描述
异常:索引越界

student类
public class Student {private String name;private int age;public Student() {}public Student(String str){//"张三,23"String[] arr = str.split("-");//arr 0 :张三,23this.name = arr[0];this.age = Integer.parseInt(arr[1]);//索引越界,把"-"改成 , 就行了}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}测试类:public class ExceptionDemo1 {public static void main(String[] args) {Student stu = new Student("张三,23");System.out.println(stu);}
}

在这里插入图片描述
异常:输入超出范围
在这里插入图片描述
在这里插入图片描述

修改:

public void setAge(int age){if(age < 18 || age > 40){throw new RuntimeException();}else {this.age=age;}
}

学生类中的赋值年龄成员函数,如果是年龄小于18或者大于40的打印输出语句提示错误,只是在控制台打印出来,并不能直接告诉调用处(代码编写处)
这样就可以有两种解决方法,第一种自己处理掉问题,或者第二种打印在控制台上

4.3 异常的处理方式

  1. JVM虚拟机默认处理异常的方式
  2. 自己处理(捕获异常)
  3. 抛出异常(交给调用者)

4.3.1 JVM默认的处理方案

在这里插入图片描述

    public static void main(String[] args) {System.out.println("狂踹瘸子那条好腿");System.out.println(2/0);System.out.println("是秃子终会发光");System.out.println("火鸡味锅巴");}

在这里插入图片描述

4.3.2 捕获异常

在这里插入图片描述
好处:

可以让程序继续往下执行,不会停止

    public static void main(String[] args) {int[] arr = {1,2,3,4,5,6};try {//可能出现异常的代码System.out.println(arr[10]);//此处出现了异常,程序就会在这里创建一个ArrayIndexOutOfBoundsException对象//然后new ArrayIndexOutOfBoundsException();//拿着这个对象到catch的小括号对比,看括号中的变量是否可以接收这个对象//如果能被接收,就表示该异常就被捕获,执行catch里面的代码//当catch里面所有的代码执行完毕,继续执行try...catch体系下面的代码}catch (ArrayIndexOutOfBoundsException e){//如果出现了ArrayIndexOutOfBoundsException异常,执行System.out.println("索引越界了");}System.out.println("看看我执行了吗");}

在这里插入图片描述

在这里插入图片描述

4.3.2 - I 如果try中没有遇到问题,怎么执行?
  • 会把try里面的代码全部执行完毕,不会执行catch里面的代码

注意:只有当出现了异常才会执行catch里面的代码

4.3.2 - II 如果try中可能会遇到多个问题,怎么执行?
  • 要写多个catch与之对应

细节:如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面

了解性:在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
表示如果出现了A异常或者B异常的话,采取同一种处理方案

4.3.2 - III 如果try中遇到的问题没有被捕获,怎么执行?
  • 相当于try…catch的代码白写了,最终还是会交给虚拟机进行处理
4.3.2 - IIIV 如果try中遇到了问题,那么try下面的其他代码还会执行吗?

下面的代码就不会运行了,直接跳转到对应的catch当中,执行catch里面的语句体

  • 但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理

小结:
在这里插入图片描述

4.3.3 异常中的常见方法

在这里插入图片描述
快捷键 ctrl + alt + t 选择try……catch包裹

1.e.getMessage

   public static void main(String[] args) {int[] arr = {1,2,3,4,5,6};try {System.out.println(arr[10]);} catch (ArrayIndexOutOfBoundsException e) {String message = e.getMessage();System.out.println(message);}System.out.println("看看我执行了吗");}

在这里插入图片描述

2.e.toString

    public static void main(String[] args) {int[] arr = {1,2,3,4,5,6};try {System.out.println(arr[10]);} catch (ArrayIndexOutOfBoundsException e) {String str = e.toString();System.out.println(str);//出现了异常的名字和异常的信息}System.out.println("看看我执行了吗");}

在这里插入图片描述
3.e.printStackTrace

在底层是利用system.err.println进行输出把异常的错误信息以红色字体输出在控制台

细节:仅仅是打印信息,不会停止程序运行

 public static void main(String[] args) {int[] arr = {1,2,3,4,5,6};try {System.out.println(arr[10]);} catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();//这个方法是没有返回值的,直接调用即可}System.out.println("看看我执行了吗");//以红色字体打印的报错System.err.println(123);}

在这里插入图片描述

4.3.4 抛出异常

在这里插入图片描述

public class Main {public static void main(String[] args) {int[] arr = {};int max = 0;try{max = getMax(arr);}catch (NullPointerException e){System.out.println("空指针异常");}catch (ArrayIndexOutOfBoundsException e){System.out.println("索引越界异常");}System.out.println(max);}public static int getMax(int[] arr) /*throws NullPointerException, ArrayIndexOutOfBoundsException 运行时异常不需要手动声明,可以不写*/{if(arr == null){//手动创建一个异常,并把这个异常交给方法的调用者处理//此时方法就会结束,下面的代码不会再执行了throw new NullPointerException();}if(arr.length == 0){throw new ArrayIndexOutOfBoundsException();}System.out.println("看看我执行了吗?");int max = arr[0];for (int i = 1; i < arr.length; i++) {if(arr[i]>max){max = arr[i];}}return max;}}

在这里插入图片描述
小结:

在这里插入图片描述

4.4 异常的综合练习

在这里插入图片描述

studnet类:public class Student {private String name;private int age;public Student() {}public Student(String str){//"张三,23"String[] arr = str.split("-");//arr 0 :张三,23this.name = arr[0];this.age = Integer.parseInt(arr[1]);//索引越界,把"-"改成 , 就行了}public Student(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}测试类:
public class ExceptionDemo1 {public static void main(String[] args) {//1.创建键盘录入的对象Scanner sc = new Scanner(System.in);//2.创建对象GirlFriend gf = new GirlFriend();//3.接收姓名//接收时最有可能出现异常,所以把其放在try中while (true) {//使用死循环,为了错误时重新录入try {System.out.println("请输入女友的名字:");String name = sc.nextLine();gf.setName(name);//接收年龄System.out.println("请输入女友年龄:");String ageStr = sc.nextLine();int age = Integer.parseInt(ageStr);gf.setAge(age);//如果所有数据正确,那么跳出循环break;} catch (NumberFormatException e) {System.out.println("年龄的格式有误,请输入数字");
//                continue;}//RuntimeException一定要写在下面,因为NumberFormatException它的爷爷就是RuntimeExceptioncatch (RuntimeException e) {System.out.println("姓名的长度或者年龄的范围有误");
//                continue;}}System.out.println(gf);}
}

在这里插入图片描述

4.5 自定义异常

自定义异常的意义就是为了让控制台的报错信息更加的见名知意

创建自定义异常

  1. 定义异常类
  2. 写继承关系
  3. 空参构造
  4. 带参构造
自定义异常NameFormatException类:
public class NameFormatException extends RuntimeException{//技巧://NameFormat:当前异常的名字,表示姓名格式化问题//Exception:表示当前类是一个异常类//运行时: RuntimeException核心就表示由于参数错误而导致的问题//编译时: Exception核心·提醒程序员检查本地信息public NameFormatException() {}public NameFormatException(String message) {super(message);}
}自定义异常AgeOutOfBoundsException类:
public class AgeOutOfBoundsException extends RuntimeException{public AgeOutOfBoundsException() {}public AgeOutOfBoundsException(String message) {super(message);}
}GirlFriend类:public class GirlFriend {private String name;private int age;public GirlFriend() {}public GirlFriend(String name, int age) {this.name = name;this.age = age;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {int len = name.length();if(len<3||len>10){throw new NameFormatException(name+"格式有误,长度应该为:3~10");}this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {if(age<18||age>40){throw new AgeOutOfBoundsException(age+"超出了范围");//把异常抛给调用者}this.age = age;}public String toString() {return "GirlFriend{name = " + name + ", age = " + age + "}";}
}public class ExceptionDemo1 {public static void main(String[] args) {//1.创建键盘录入的对象Scanner sc = new Scanner(System.in);//2.创建对象GirlFriend gf = new GirlFriend();//3.接收姓名//接收时最有可能出现异常,所以把其放在try中while (true) {//使用死循环,为了错误时重新录入try {System.out.println("请输入女友的名字:");String name = sc.nextLine();gf.setName(name);//接收年龄System.out.println("请输入女友年龄:");String ageStr = sc.nextLine();int age = Integer.parseInt(ageStr);gf.setAge(age);//如果所有数据正确,那么跳出循环break;} catch (NumberFormatException e) {System.out.println("年龄的格式有误,请输入数字");
//                continue;}catch (NameFormatException e) {e.printStackTrace();}catch (AgeOutOfBoundsException e) {e.printStackTrace();}}System.out.println(gf);}
}

在这里插入图片描述

5. File

File对象就表示一个路径,可以是文件的路径、也可以是文件夹的路径
这个路径可以是存在的,也允许是不存在的

在这里插入图片描述

“C:\Users\xiaoyou\Desktop\a.txt”为例

  • 父级路径就是“C:\Users\xiaoyou\Desktop”
  • 子级路径就是“a.txt”

5.1 File的构造方法

在这里插入图片描述

  public static void main(String[] args) {//1.根据字符串表示的路径,变成File对象String str = "C:\\Users\\xiaoyou\\Desktop\\a.txt";File f1 = new File(str);System.out.println(f1);//2.父级路径:C:\Users\xiaoyou\Desktop//子级路径:a.txtString parent =  "C:\\Users\\alienware\\Desktop";String child = "a.txt";File f2 = new File(parent,child);System.out.println(f2);File f3 = new File(parent+"\\"+child);System.out.println(f3);//3.把一个File表示的路径和String表示路径进行拼接File parent2 = new File("C:\\Users\\alienware\\Desktop");String child2 = "b.txt";File f4 = new File(parent2,child2);System.out.println(f4);}

小结:
在这里插入图片描述

5.2 File的常见成员方法

5.2.1 判断和获取相关的

在这里插入图片描述
文件图:
在这里插入图片描述

1.判断的方法

    public static void main(String[] args) {//1.对一个文件的路径进行判断File F1 = new File("F:\\aaa\\a.txt");System.out.println(F1.isDirectory());//falseSystem.out.println(F1.isFile());//trueSystem.out.println(F1.exists());//trueSystem.out.println("--------------------------");//2.对一个文件夹的路径进行判断File f2 = new File("F:\\aaa\\bbb");System.out.println(f2.isDirectory());//trueSystem.out.println(f2.isFile());//falseSystem.out.println(f2.exists());//true//3.对一个不存在的路径进行判断//shift + f6 一键改名File f3 = new File("F:\\aaa\\c.txt");System.out.println(f3.isDirectory());//falseSystem.out.println(f3.isFile());//falseSystem.out.println(f3.exists());//false}

2.获取的方法

public static void main(String[] args) {//1.length返回文件的大小(字节数量)//细节1: 这个方法只能获取文件的大小,单位是字节//如果单位我们要是M,G,可以不断的除以1024//细节2: 这个方法无法获取文件夹的大小//如果我们要获取一个文件夹的大小,需要把这个文件夹里面所有的文件大小都累加在一起File F1 = new File("F:\\aaa\\a.txt");long len = F1.length();System.out.println(len);//0//文件夹File f2 = new File("F:\\aaa\\bbb");long len2 = f2.length();System.out.println(len2);System.out.println("----------------------------");//2.getAbsolutePath 返回文件的绝对路径File f3 = new File("F:\\aaa\\a.txt");String path1 = f3.getAbsolutePath();System.out.println(path1);//在当前模块下又新建一个a.txt文件File f4 = new File("javaprogram1\\a.txt");String path2 = f4.getAbsolutePath();System.out.println(path2);System.out.println("----------------------------");//3.getPath 返回定义文件时使用的路径File f5 = new File("F:\\aaa\\a.txt");String path3 = f5.getPath();System.out.println(path3);//在当前模块下又新建一个a.txt文件File f6 = new File("javaprogram1\\a.txt");String path4 = f6.getPath();System.out.println(path4);System.out.println("----------------------------");//4.getName 获取名字//a.txt//  a 文件名//  txt 后缀名、扩展名File f7 = new File("F:\\aaa\\a.txt");String name1 = f7.getName();System.out.println(name1);//文件夹File f8 = new File("F:\\aaa\\bbb");String name2 = f8.getName();System.out.println(name2);System.out.println("----------------------");//5.lastModified 返回文件的最后修改实际(时间毫秒值)File f9 = new File("F:\\aaa\\a.txt");long time = f9.lastModified();System.out.println(time);System.out.println("-----------------------");//如何把时间的毫秒值变成字符串表示的时间呢?//yyyy年MM月dd日 HH:mm:ssSimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");String timeStr = sdf.format(time);System.out.println(timeStr);}

在这里插入图片描述

5.2.2 创建和删除相关的

在这里插入图片描述
1.创建相关的

    public static void main(String[] args) throws IOException {//1.createNewFile 创建一个新的空文件//细节1:如果当前路径表示的文件是不存在的,则创建成功,返回true//      如果当前路径表示的文件是存在的,则创建失败,返回false//细节2:如果父级路径是不存在的,那么方法会有异常IOException//细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件File f1 = new File("F:\\aaa\\c.txt");boolean b = f1.createNewFile();//代码写完会有红色异常,alt回车选择签名抛出即可System.out.println(b);//true//2.mkdir    make Directory. 文件夹(目录)//细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false//细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹File f2 = new File("F:\\aaa\\aaa\\bbb\\ccc");boolean b2 = f2.mkdir();System.out.println(b2);//false//3.mkdirs  创建多级文件夹//细节:既可以创建单级的,又可以创建多级的文件夹File f3 = new File("F:\\aaa\\aaaa\\bbb\\ccc");boolean b3 = f3.mkdirs();System.out.println(b3);//true}

在这里插入图片描述

2.删除相关的

    public static void main(String[] args) throws IOException {//1.delete     删除文件、空文件//细节1://      如果刖除的是文件,则直接删除,不走回收站。//      如果别除的是空文件夹,则直接删除,不走回收站//      如果别除的是有内容的文件夹,则删除失败File f1 = new File("F:\\aaa\\bbb");//2.删除boolean b = f1.delete();System.out.println(b);}

5.2.3 获取并遍历相关的

在这里插入图片描述

    public static void main(String[] args){File f = new File("F:\\aaa");//listFiles方法//作用:获取aaa文件夹里面的所有内容,把所有的内容放到数组中返回File[] files = f.listFiles();for (File file : files) {//file依次表示aaa文件夹里面的每一个文件或者文件夹System.out.println(file);}}

在这里插入图片描述

在这里插入图片描述

5.2.4 所有遍历并获取的方法

在这里插入图片描述

   public static void main(String[] args){//1.listRoots  获取系统中所有的盘符File[] arr = File.listRoots();System.out.println(Arrays.toString(arr));System.out.println("----------------------------------");//2.list()  获取当前该路径下所有内容(仅仅能获取名字)File f1 = new File("F:\\aaa");String[] arr2 = f1.list();for (String s : arr2) {System.out.println(s);}System.out.println("--------------------------");//3.list(FilenameFilter filter)  利用文件名过滤器获取当前该路径下所有内容//需求:我现在要获取F:\\aaa 文件夹里面所有的txt文件File f2 = new File("F:\\aaa");//accept方法的形参,依次表示aaa文件夹里面的每一个文件或者文件夹路径的路径/*参数一:父级路径参数二:子级路径返回值:如果返回值true,就表示当前路径保留如果返回值false,就表示当前路径舍弃不要*/String[] arr3 = f2.list(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {File src = new File(dir,name);return src.isFile()&&name.endsWith(".txt");}});System.out.println(Arrays.toString(arr3));System.out.println("-----------------------------");//4.listFiles 获取当前该路径下所有内容File f3 = new File("F:\\aaa");//需求:打印里面所有的内容File[] arr4 = f3.listFiles();for (File file : arr4) {if(file.isFile()&&file.getName().endsWith(".txt")){System.out.println(file);}}System.out.println("-----------------------------");//5.listFiles(FileFilter filter) 利用文件名过滤器获取当前该路径下所有内容File f4 = new File("F:\\aaa");//调用listFilesFile[] arr5 = f4.listFiles(new FileFilter() {@Overridepublic boolean accept(File pathname) {return pathname.isFile()&&pathname.getName().endsWith(".txt");}});System.out.println(Arrays.toString(arr5));System.out.println("-----------------------------");//6.listFiles(FilenameFilter filter)//与上面方法5不同的是,上面形参表示的是完整的路径//这个形参把一个大的路径拆开,第一个参数表示父级路径,第二个参数表示子级路径File[] arr6 = f4.listFiles(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {File src = new File(dir, name);return src.isFile() && name.endsWith(".txt");}});System.out.println(Arrays.toString(arr6));}

在这里插入图片描述

5.2.5 综合练习

练习一:创建文件

在这里插入图片描述

    public static void main(String[] args) throws IOException {//1.创建b.txt的父级路径File file = new File("javaprogram1");//2.创建父级路径//如果bbb是存在的,那么此时创建失败//如果bbb是不存在的,那么此时创建成功file.mkdirs();//3.拼接父级路径和子级路径File src = new File(file, "b.txt");boolean b = src.createNewFile();if(b){System.out.println("创建成功");}else {System.out.println("创建失败");}}
//创建成功,那么如果再运行依次就会创建失败
练习二:单个文件夹查找文件

在这里插入图片描述

public class Main {public static void main(String[] args) throws IOException {File file = new File("F:\\aaa");boolean b = haveAVI(file);System.out.println(b);}/*作用:用来找到某一个文件夹中,是否有以avi结尾的电影形参:要查找的文件夹返回值:查找的结果 存在true 不存在false*/public static boolean haveAVI(File file){//F:\\aaa//1.进入aaa文件夹,而且要获取里面的所有内容File[] files = file.listFiles();//2.遍历数组获取里面的每一个元素for (File f : files) {if(f.isFile()&&f.getName().endsWith(".avi")){return true;}}//3.如果循环结束之后还没有找到,直接返回truereturn false;}
}//false
练习三:遍历硬盘查找文件夹

在这里插入图片描述
采用递归,把大问题拆成一个个小问题
在这里插入图片描述
所有文件夹的套路:

  1. 进入文件夹
  2. 遍历数组
  3. 判断,如果是文件,就可以执行题目的业务逻辑
  4. 再判断,如果是文件夹,就可以递归
    重要:再次调用本方法的时候,参数一个要是src的次一级路径
  5. 注意:

当有获取文件夹数组遇到没有权限访问的时候,可能为空,那么遍历的时候就会导致空指针异常,所以遍历的时候要做一个非空判断执行

public class Main {public static void main(String[] args) {File src =new File("F:\\");FindAVI(src);findAVI();}public static void findAVI(){//获取本地所有的盘符File src =new File("F:\\");File[] arr = src.listRoots();for (File file : arr) {FindAVI(file);}}public static void FindAVI(File src){//1.进入文件夹srcFile[] files = src.listFiles();if(files != null){//2.遍历数组,依次得到src里面的每一个文件或文件夹for (File file : files) {if(file.isFile()){//3.判断,如果是文件,就可以执行题目的业务逻辑String name = file.getName();if(name.endsWith(".txt")){System.out.println(file);}}else{//4.再判断,如果是文件夹,就可以递归FindAVI(file);}}}}
}

在这里插入图片描述

练习四:删除文件夹

在这里插入图片描述
删除一个多级文件夹:
如果我们要删除一个有内容的文件夹

  1. 先删除文件夹里面所有的内容
  2. 再删除自己
public class Main {public static void main(String[] args) {File src = new File("F:\\aaa\\ccc");Delete(src);}public static void Delete(File src){//1.先删除文件夹里面的所有内容//进入srcFile[] files = src.listFiles();if(files!=null){//遍历文件数组for (File file : files) {//判断,如果是文件则删除if(file.isFile()){file.delete();System.out.println("删除成功");}else {//如果是文件夹则递归Delete(file);}}}//2.遍历完后,再删除自己src.delete();}}

图解示例:
在这里插入图片描述

练习五:统计文件夹大小

在这里插入图片描述
作用:统计一个文件夹的总大小
参数:表示要统计的那个文件夹
返回值:统计之后的结果
文件夹的总大小:说白了,文件夹里面的所有文件的大小

图示:
在这里插入图片描述

public class Main {public static void main(String[] args) {File src = new File("F:\\aaa\\ccc");System.out.println(getLen(src)+"字节");}public static long getLen(File src){//1.定义变量进行累加统计long len = 0;//2.进入src文件夹File[] files = src.listFiles();//3.遍历for (File file : files) {//4.判断if (file.isFile()){//是文件,我们就把当前文件的大小累加到len当中len = len + file.length();}else {//不是文件,是文件夹就递归len+getLEN//如果直接递归调用getLen(file)不累加,那么每次递归时len都会刷新,无法看到总长度len = len + getLen(file);}}//循环结束后返回lenreturn len;}}

在这里插入图片描述

在这里插入图片描述

练习六:统计各种文件夹数量

在这里插入图片描述
作用:统计一个文件夹中每种文件的个数
参数:要统计的那个文件夹
返回值:用来统计map集合

  • 键:后缀名 值:次数
    a.txt
    a.a.txt
    aaa(不需要统计的)
public class Main {public static void main(String[] args) {File file = new File("F:\\aaa");HashMap<String, Integer> hm = getCount(file);System.out.println(hm);}public static HashMap<String,Integer> getCount(File src){//1.定义集合用来统计HashMap<String,Integer> hm = new HashMap<>();//2.进入src文件夹File[] files = src.listFiles();//3.遍历数组for (File file : files) {//4.判断,如果是文件,统计if(file.isFile()){//a.txtString name = file.getName();//按照 \\. 进行切割,.前后分开String[] arr = name.split("\\.");//arr如果没有后缀名,数组长度为1,这种情况是不需要判断的//arr的长度>=2,说明最后一个索引上的元素才是后缀名if(arr.length>=2){String endName = arr[arr.length - 1];//最终索引上的元素//把后缀名到集合里面进行判断if(hm.containsKey(endName)){//存在,就把已经出现的次数拿出来,进行自增int count = hm.get(endName);count++;//再把自增的结果放进map集合里的键值中hm.put(endName,count);}else{//不存在,表示当前文件是第一次出现hm.put(endName,1);}}}else{//5.如果是文件夹,递归//sonMap里面是子文件中每一种文件的个数HashMap<String,Integer> sonMap = getCount(file);//假如:hm:txt=1 jpg=2 doc=3//sonMap: txt=3 jpg=1//其实遍历sonMap把里面的值累加到hm当中Set<Map.Entry<String, Integer>> entries = sonMap.entrySet();for (Map.Entry<String, Integer> entry : entries) {String key = entry.getKey();//sonMap的keySystem.out.println(entry);//txt=1System.out.println(key);//txtSystem.out.println(sonMap);//{txt=1}System.out.println(hm);//{txt=3}System.out.println(hm.get(key));//3int value = entry.getValue();System.out.println(value);//1System.out.println("---------------------------");//拿着key放到hm集合中判断是否存在if(hm.containsKey(key)){//存在,则把该value和hm的value进行累加//比如hm的txt 1 + sonMap的txt 3int count = hm.get(key);System.out.println(count);System.out.println(hm);System.out.println(value);count = count + value;//核心hm.put(key,count);}else {//键不存在,直接放进hm集合中hm.put(key,value);}}}}return hm;}
}

在这里插入图片描述
在这里插入图片描述

相关文章:

小黑子—Java从入门到入土过程:第八章

Java零基础入门8.0 Java系列第八章1. 双列集合 Map1.1 Map 集合中常见的API1.2 Map 集合的遍历方式1.2 - I 第一种遍历方式&#xff1a;键找值KeySet 方法1.2 - II 第二种遍历方式&#xff1a;键值对 entrySet 方法1.2 - III 第三种遍历方式&#xff1a;lambda表达式 1.3 HashM…...

innodb_flush_log_at_trx_commit 和 sync_binlog 参数解析

这两个参数和MySQL的一致性以及性能相关&#xff0c;默认配置大多数情况下不是最优的。一般来说&#xff0c;互联网线上系统的配置&#xff1a; innodb_flush_log_at_trx_commit —— 0 sync_binlog —— 1000 一、innodb_flush_log_at_trx_commit 事务提交刷盘时机 如果我…...

hd debug - DAPLink的资料

文章目录 DAPLink的资料概述笔记库迁出的技巧END DAPLink的资料 概述 查资料时, 看到有DAPLink的资料, 记录一下. 笔记 DAPLink项目分为软件和硬件2部分, 不在一个库中. 总览 : https://daplink.io/ 这个页面上说了软件和硬件项目的库地址. 软件库地址 : https://github.…...

Android adb常用50条命令

1. adb devices - 列出所有连接的 Android 设备及模拟器 2. adb shell - 启动 Android 设备或模拟器的 shell 终端 3. adb install - 安装 APK 文件 4. adb uninstall - 卸载 APK 文件 5. adb logcat - 查看日志输出信息,用于调试应用 6. adb push - 将文件推送到 Andro…...

【无人车】无人驾驶地面车辆避障研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Visual Studio高效调试手段与调试技巧总结

目录 1、对0xCCCCCCCC、0xCDCDCDCD和0xFEEEFEEE等常见异常值的辨识度 2、在Debug下遇到报错弹框,点击重试,查看函数调用堆栈...

Day37 Map集合

Map集合 Map集合是接口&#xff0c;interface Map <K , V> K:键的类型&#xff1b; V&#xff1a;值的类型 将键映射到值得对象&#xff1b;不能包含重复的键&#xff1b;每个键可以映射到最多一个值。例如&#xff1a;001 令狐冲 &#xff1b; 002 岳不群 &#xff1b; …...

是人就能学会的Spring源码教学-Spring的简单使用

是人就能学会的Spring源码教学-Spring的简单使用 Spring的最简单入门使用第一步 创建项目第二步 配置项目第三步 启动项目 Spring的最简单入门使用 各位道友且跟我一道来学习Spring的最简单的入门使用&#xff0c;为了方便和简单&#xff0c;我使用了Spring Boot项目&#xff…...

NOC大赛·核桃编程马拉松赛道知识点大纲(高年级及初中组)

NOC核桃编程马拉松知识点大纲(高年级及初中组) (一)基础语法 1.掌握运动积木的用法。 包括“移动 10 步”、“左/右转 X 度”、“面向 X 方向/鼠标指针/ 角色”、“移到 XY 坐标/鼠标/角色”、“X/Y 坐标的设定和增加”、 “滑行到 XY/鼠标/角色”等积木用法,详细如下。 1…...

第二十六章 Unity碰撞体Collision(上)

在游戏世界中&#xff0c;游戏物体之间的交互都是通过“碰撞接触”来进行交互的。例如&#xff0c;攻击怪物则是主角与怪物的碰撞&#xff0c;触发机关则是主角与机关的碰撞。在DirectX课程中&#xff0c;我们也大致介绍过有关碰撞检测的内容。游戏世界中的3D模型的形状是非常复…...

Qt Installer Framework使用教程:

步骤一&#xff1a; 下载并安装Qt Installer Framework工具 http://download.qt.io/official_releases/qt-installer-framework/ 将安装目录添加到环境变量&#xff0c;如安装D盘时D:\Qt\QtIFW-4.5.0\bin 步骤二&#xff1a; 将测试例子(如D:\Qt\QtIFW-4.5.0\…...

nodejs+vue+java农村信息化服务平台

用户的登录模块&#xff1a;用户登录本系统&#xff0c;对个人的信息等进行查询&#xff0c;操作可使用的功能。 用户注册模块&#xff1a;游客用户可以进行用户注册&#xff0c;系统会反馈是否注册成功。 添加管理员模块&#xff1a;向本系统中添加更多的管理人员&#xff0c;…...

代码随想录补打卡 62不同路径 63 不同路径2

代码如下 func uniquePaths(m int, n int) int { dp : make([][]int,m) //定义一个二维数组 for i : 0 ; i < m ; i { //遍历这个二维数组的每个元素&#xff0c;并将每个元素都定义为一个一维数组 dp[i] make([]int,n) //这样就生成了图中的一个二维网格 dp[i][0] …...

树的存储和遍历

文章目录 6.5 树与森林6.5.1 树的存储结构1. 双亲表示法(顺序存储结构)2 孩子链表表示法3 孩子兄弟表示法(二叉树表示法) 6.5.2 森林与二叉树的转换1 树转换成二叉树2 二叉树转换成树3 森林转换成二叉树4 二叉树转换成森林 6.5.3 树和森林的遍历1. 树的遍历2. 森林的遍历 6.6 赫…...

MySQL的ID用完了,怎么办?

目 录 一 首先首先分情况 二 自增ID 1 mysql 数据库创建一个自增键的表 2 导出表结构 3 重新创建 自增键是4294967295的表 4 查看表结构 5 异常测试 三 填充主键 1 首先创建一个test 表&#xff0c;主键不自增 2 插入主键最大值 3 再次插入主键最大值1 四 没有声明…...

JSP基于Iptables图形管理工具的设计与实现(源代码+论文)

Netfilter/Iptables防火墙是Linux平台下的包过滤防火墙&#xff0c;Iptables防火墙不仅提供了强大的数据包过滤能力&#xff0c;而且还提供转发&#xff0c;NAT映射等功能&#xff0c;是个人及企业级Linux用户构建网络安全平台的首选工具。但是&#xff0c;由于种种原因&#x…...

什么是数据库分片?

什么是数据库分片&#xff1f; 数据库分片是指将一个大型数据库拆分成多个小型数据库&#xff0c;每个小型数据库称为一个分片。通过这种方式&#xff0c;可以将数据库的负载分散到多个服务器上&#xff0c;从而提高数据库的性能和可伸缩性。 为什么需要数据库分片&#xff1f…...

软件工程知识点

软件工程提出的时代和背景 软件工程化的层次 软件开发过程 敏捷与计划开发 演化式开发和DevOps 软件配置管理过程和相关工具名 软件质量和代码异味 能够分析常见的代码异味和bug 代码复杂度和计算圈复杂度 了解代码异味和重构行为的关系 了解自动化单元测试框架xunit…...

华为OD机试 - 投篮大赛(Python)

题目描述 你现在是一场采用特殊赛制投篮大赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。 比赛开始时,记录是空白的。 你会得到一个记录操作的字符串列表 ops,其中ops[i]是你需要记录的第i项操作,ops遵循下述规则: 整数x-表示本回合…...

《花雕学AI》讯飞星火认知大模型的特点和优势,与ChatGPT的对比分析

引言&#xff1a; 人工智能是当今科技领域的热门话题&#xff0c;自然语言处理是人工智能的重要分支。自然语言处理的目标是让计算机能够理解和生成自然语言&#xff0c;实现人机交互和智能服务。近年来&#xff0c;随着深度学习的发展&#xff0c;自然语言处理领域出现了许多创…...

【Python入门】Python的判断语句(if else 语句)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…...

(4.28-5.4)【大数据新闻速递】数字中国峰会成功举办;“浙江数据知识产权登记平台”上线;贵州大数据活跃;AI教父从谷歌离职

01【2023年数字中国建设峰会数字福建分论坛成功举办】 2023年数字中国建设峰会数字福建分论坛由福建省人民政府主办&#xff0c;福建省数字福建建设领导小组办公室、数字中国研究院&#xff08;福建&#xff09;和福建省大数据集团承办。分论坛于2023年4月28日下午14:30 -17:3…...

领域驱动设计(Domain Driven Design)之建立领域模型

文章目录 用领域模型表达领域概念建立模型使用通用语言验证模型识别构造块类型设计聚合如何使用领域模型再次思考总结用领域模型表达领域概念 在实际项目中,模型设计者往往过早陷入具体构造块类型的识别,比如实体、聚合、领域服务,而忽略了领域模型表达领域概念的目的。我们…...

有研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具(下)

导语&#xff1a;研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具。 Apple EFI IM4P分配器 介绍 解析苹果多个EFI固件.im4p文件&#xff0c;并将所有检测到的EFI固件分割为单独的SPI/BIOS映像。 使用 你可以拖放或手动输入包含Apple EFI IM4P固件的文件夹的完整路径。…...

iOS开发系列--Swift语言

概述 Swift是苹果2014年推出的全新的编程语言&#xff0c;它继承了C语言、ObjC的特性&#xff0c;且克服了C语言的兼容性问题。Swift发展过程中不仅保留了ObjC很多语法特性&#xff0c;它也借鉴了多种现代化语言的特点&#xff0c;在其中你可以看到C#、Java、Javascript、Pyth…...

【MOMO】高水平期刊目录(持续更新)

高水平期刊目录 引言1 顶级期刊目录&#xff08;A&#xff09;1.1 IEEE Transactions on Intelligent Transportation Systems1.2 IEEE Transactions on Neural Networks and Learning Systems1.3 Engineering 2 权威期刊目录&#xff08;A&#xff09;2.1 Measurement 3 鼓励期…...

LVS负载均衡集群--DR模式

一、LVS-DR集群介绍 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09;工作模式&#xff0c;是生产环境中最常用的一 种工作模式。 1、LVS-DR 工作原理 LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff0…...

RabbitMQ --- 死信交换机

一、简介 1.1、什么是死信交换机 什么是死信&#xff1f; 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff08;dead letter&#xff09;&#xff1a; 消费者使用basic.reject或 basic.nack声明消费失败&#xff0c;并且消息的requeue参数设置为false…...

如何在个人web项目中使用会话技术(cookiesession)?

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 服务器软件&#xff1a;apache-tomcat-8.5.27 目录 一. 什么是会话&#xff1f;二. 为什么要使用会话技术&#xff1f;三. 如何使用会话技术&#xff1f;3.1 Cookie(客户端的会话技术…...

创建线索二叉树

创建线索二叉树 一、创建线索二叉树一、案例1、前序线索二叉树2、中序线索二叉树3、后序线索二叉树 一、创建线索二叉树 现将某结点的空指针域指向该结点的前驱后继&#xff0c;定义规则如下&#xff1a; 若结点的左子树为空&#xff0c;则该结点的左孩子指针指向其前驱结点。…...

wordpress搭建网站/腾讯广告投放推广平台价格

想测试STM32F429 和linux USB 串口速率&#xff0c;网上讲了各种软件好像都不能用&#xff0c;wireshark 的USB cap 也不管用&#xff0c;干脆自己写一个程序来测吧&#xff01; 关于测试程序执行时间的方法在测试串口时也不管用&#xff08;clock&#xff0c;time 等等&#x…...

惠州营销网站制作/网络宣传推广方法

普罗米修斯&#xff1a;Prometheus是一个开放性的监控解决方案&#xff0c;用户可以非常方便的安装和使用Prometheus并且能够非常方便的对其进行扩展 下面将实现一个SpringBoot应用接入Prometheus的全过程 1.2 安装 Linux 安装 官网指定下载包: https://prometheus.io/down…...

医疗美容网站模板/企业网站怎么注册官网

svg复用元素的方式主要有 <g>, <defs>, <symbol>, <use> 1. <g> group, 分组&#xff0c;定义一组元素&#xff0c;初始不可见 <g id"group" fill"red"><rect x"10" y"10" width"100&quo…...

设计网站建设书南昌/海南百度推广总代理

CASE 可能是 SQL 中被误用最多的关键字之一。虽然你可能以前用过这个关键字来创建字段&#xff0c;但是它还具有更多用法。例如&#xff0c;你可以在 WHERE 子句中使用 CASE。 首先让我们看一下 CASE 的语法。在一般的 SELECT 中&#xff0c;其语法如下&#xff1a; SELECT &l…...

股票交易网站建设/网络产品及其推广方法

让我们来看一下与数字签名并行的一个有用技巧&#xff0c;基本想法是从数字签名模式中拿出一个公共验证密钥&#xff0c;并将其与一个 人或一个系统参与者的身份对等。如果你见到一条消息的签名被公钥pk正确验证&#xff0c;那么你可以认为pk就是在表达这条消息。你真的可以将公…...

展览设计制作公司/北京百度搜索排名优化

最常用的Eclipse的快捷键&#xff0c;各个实用&#xff0c;由Java攀登网提供。 虽然现在IDEA非常的好用&#xff0c;但是Eclipse还是有一定的受众用户&#xff0c;包括我现在的公司&#xff0c;领导就是让用Eclipse&#xff0c;因此喜欢Eclipse的朋友可以看下。 后面可以再讲…...