目录
一、可变参数
1.1 可变参数的概述和注意事项
可变参数介绍 java1.5之后 - 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了 - 方法的参数类型已经确定,个数不确定,我们可以使用可变参数 - 可变参数定义格式 ```java 修饰符 返回值类型 方法名(数据类型… 变量名) { } ``` - 可变参数的注意事项 - 可变参数的本质其实是一个数组 - 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
案例1
public class Demo01 {
public static void main(String[] args) {
System.out.println(sum(1,2,3,4,5));
}
public static int sum(int... a) {
int sum = 0;
for(int i : a) {
sum += i;
}
return sum;
}
}
案例2
public class Demo02 {
public static void main(String[] args) {
int index1 = getIndex(4, 1, 2, 3, 4, 5, 6);
System.out.println(index1);
}
//如果有多个参数,可变参数放在最后
public static int getIndex(int num, int... arr) {
int index = -1;
for (int i = 0; i < arr.length; i++) {
if (num == arr[i]) {
index = i;
break;
}
}
return index;
}
}
1.2 创建不可变集合
可变参数的应用场景: 创建不可变集合 将参数中的数据封装成一个集合, 该集合的特点是长度和内容都不能发生改变
创建不可变集合
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合
- 1.这个集合不能添加,不能删除,不能修改
2.set 中不能添加重复的元素
- 3.但是可以结合集合的带参构造,实现集合的批量添加
- 在Map接口中,还有一个ofEntries方法可以提高代码的阅读性
- 首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中
创建不可变集合的方法 1. static<E> List<E> of(E...elements); 创建一个具有指定元素的List集合对象 2. static<E> Set<E> of(E...elements); 创建一个具有指定元素的Set集合对象 3. static<K,V> Map<K,V> of(E...elements); 创建一个具有指定元素的Map集合对象 ofEntries(Map.entry(K1,V1),Map.entry(K2,V2)..); 提升代码的阅读性
public class Demo {
public static void main(String[] args) {
method1();
method2();
method3();
}
public static void method1() {
//static <K , V> Map<K,V> of(E…elements) 创建一个具有指定元素的Map集合对象
Map<String, String> map = Map.of("12", "23", "45", "67");
System.out.println(map); //{12=23, 45=67}
//在Map接口中,还有一个ofEntries方法可以提高代码的阅读性
// 首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中
Map<String, String> map1 = Map.ofEntries(Map.entry("666", "777"), Map.entry("888", "999"));
System.out.println(map1); //{888=999, 666=777}
}
// static <E> List<E> of(E…elements) 创建一个具有指定元素的List集合对象
public static void method2() {
List<String> list = List.of("111", "222", "333");
System.out.println(list); //[111, 222, 333]
//集合的批量添加
//首先是通过调用List.of方法来创建一个不可变的集合,of方法的形参就是一个可变参数。
//再创建一个ArrayList集合,并把这个不可变的集合中所有的数据,都添加到ArrayList中。
ArrayList<String> list1 = new ArrayList<>(List.of("9999", "66666", "55555"));
System.out.println(list1); //[9999, 66666, 55555]
}
//static <E> Set<E> of(E…elements) 创建一个具有指定元素的Set集合对象,不能添加重复元素
public static void method3() {
Set<String> set = Set.of("00", "11", "22", "33");
System.out.println(set); //[22, 33, 00, 11]
}
}
二、Stream流
2.1 Stream流的思想
流水线操作 瓶子 -> 检查瓶子 -> 消毒 -> 罐装 -> 密封 -> 包装 Stream流理解为代码流水线 获取方法(获取Stream流将数据放在流水线) -> 中间方法(过滤操作等) -> 终结方法(执行后就不能调用其他方法了)
2.2 Stream流的特点
Stream流的好处 - 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印 - Stream流把真正的函数式编程风格引入到Java中 - 代码简洁
注意:Stream流只能修改流里面的内容,不能直接修改数据源中的数据
2.3 Stream流的方法
Stream流的三类方法 - 获取Stream流 - 创建一条流水线,并把数据放到流水线上准备进行操作 - 中间方法 - 流水线上的操作 - 一次操作完毕之后,还可以继续进行其他操作 - 终结方法 - 一个Stream流只能有一个终结方法 - 是流水线上的最后一个操作
2.3.1 Stream流的获取方法
第一类:获取方法, 操作的数据类型不同, 获取方法也不同 1. 单列集合: 使用Collection中的默认方法stream()生成流 2. 双列集合: 通过ketSet或者entrySet获取Set集合, 再获取Stream流 (间接获取) 3. 数组: Arrays中的静态方法stream()生成流 4. 同种数据类型的数个数据: 通过Stream.of(T...values)生成流, 参数是可变参数
public class Demo02 {
public static void main(String[] args) {
method1();
method2();
method3();
method4();
}
//同一类型的多个数据
//通过Stream接口的静态方法of(T... values)生成流
public static void method4() {
Stream.of(1,2,3,4,5,6,7,8).forEach(a-> System.out.println(a));//1 2 3 4 5 6 7 8 9
}
//数组
//通过Arrays中的静态方法stream生成流
public static void method3() {
int[] arr = {1, 2, 3, 4};
Arrays.stream(arr).forEach(num -> System.out.println(num));//1 2 3 4
}
//双列集合
//Map体系集合
//把Map转成Set集合,间接的生成流
public static void method2() {
HashMap<String, String> map = new HashMap<>();
map.put("444", "666");
map.put("111", "222");
map.put("777", "888");
map.keySet().stream().forEach(k-> System.out.println(k));//111 444 777
map.entrySet().stream().forEach(kv-> System.out.println(kv));//111=222 444=666 777=888
}
//单列集合
//Collection体系集合
//使用默认方法stream()生成流, default Stream<E> stream()
public static void method1() {
ArrayList<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
list.stream().forEach(s -> System.out.println(s));//123 456 789
}
}
2.3.2 Stream流的中间方法-filter
Stream<T> filter(Predicate predicate)用于对流中的数据进行过滤
public class Demo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(List.of("张三", "李四", "王麻子", "二流子", "大混子"));
//Stream<T> filter(Predicate predicate)用于对流中的数据进行过滤
list.stream().filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
//王麻子 二流子 大混子
//ofEntries方法可以提高代码的阅读性
HashMap<String, String> map = new HashMap<>(Map.ofEntries(Map.entry("张三", "789"), Map.entry("李四", "456")));
map.keySet().stream().forEach(k -> System.out.println(k));//李四 张三
map.entrySet().stream().forEach(kv-> System.out.println(kv));//李四=456 张三=789
}
}
2.3.3 Stream流的其他中间方法
截取 Stream<T> limit(long maxSize)
跳过 Stream<T> skip(long n)
去重 Stream<T> distinct()
自定义类,要去重的话,需重写equals nad hashcode方法
合并 static <T> Stream<T> concat(Stream a, Stream b)
//自定义类,要去重的话,需重写equals nad hashcode方法
public class Demo {
public static void main(String[] args) {
ArrayList<Student> list1 = new ArrayList<>();
list1.add(new Student("张三", 23));
list1.add(new Student("李四", 24));
list1.add(new Student("王五", 25));
list1.add(new Student("王五", 25));
list1.add(new Student("二狗子", 23));
ArrayList<Student> list2 = new ArrayList<>();
list2.add(new Student("王麻子", 18));
list2.add(new Student("二流子", 17));
list2.add(new Student("大混子", 17));
list2.add(new Student("大黑娃", 19));
//Stream<T> limit(long maxSize)返回此流中的元素组成的流,截取前指定参数个数的数据
list1.stream().limit(2).forEach(s -> System.out.println(s));
/*
Student{name='张三', age=23}
Student{name='李四', age=24}
*/
//Stream<T> skip(long n)跳过指定参数个数的数据,返回由该流的剩余元素组成的流
list1.stream().skip(4).forEach(s -> System.out.println(s));
/*
Student{name='二狗子', age=23}
*/
//在自定义类,要去重的话,需重写equals nad hashcode方法
//Stream<T> distinct()去重,返回由该流的不同元素(根据Object.equals(Object) )组成的流
list1.stream().distinct().forEach(s -> System.out.println(s));
/*
Student{name='张三', age=23}
Student{name='李四', age=24}
Student{name='王五', age=25}
Student{name='二狗子', age=23}
*/
//static <T> Stream<T> concat(Stream a, Stream b)合并a和b两个流为一个流
Stream.concat(list1.stream(), list2.stream()).forEach(m -> System.out.println(m));
/*
Student{name='张三', age=23}
Student{name='李四', age=24}
Student{name='王五', age=25}
Student{name='王五', age=25}
Student{name='二狗子', age=23}
Student{name='王麻子', age=18}
Student{name='二流子', age=17}
Student{name='大混子', age=17}
Student{name='大黑娃', age=19}
*/
}
}
class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Student{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append('}');
return sb.toString();
}
}
2.3.4 Stream流的终结方法
终结方法 void forEach(Consumer action)对此流的每个元素执行操作 long count()返回此流中的元素数终结方法: 一个Stream流只能有一个终结方法, 该方法执行后就不能调用其他方法了
public class Demo {
public static void main(String[] args) {
Set<String>set = new TreeSet<>();
set.add("123");
set.add("456");
set.add("456");
set.add("789");
set.add("000");
//Set.of 不能添加重复元素,否则会报错
Set<String>set1 = new TreeSet<>(Set.of("111","222","123"));
//void forEach(Consumer action)对此流的每个元素执行操作
set.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);000 123 456 789
}
});
set1.stream().forEach((String s)-> System.out.println(s));//111 123 222
//long count()返回此流中的元素数
System.out.println(set.stream().count());//4
}
}
2.3.5 Stream流的收集方法
思考 流操作只能修改流中的数据, 不能直接修改数据源中的数据 使用中间方法操作完毕后, 想将数据存储收集起来, 该怎么办呢?
收集方法 概念 对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中 常用方法 R collect(Collector collector)把结果收集到集合中 工具类Collectors提供了具体的收集方式 public static <T> Collector toList()把元素收集到List集合中 public static <T> Collector toSet()把元素收集到Set集合中 public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合中
1.toSet toList方法
1. toSet toList方法
public class Demo01 {
public static void main(String[] args) {
//添加方式1 List.of
ArrayList<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
//添加方式2
list.add(10);
list.add(10);
list.add(10);
ArrayList<Integer> list1 = new ArrayList<>();
//添加方式3
for (int i = 1; i <= 10; i++) {
list1.add(i);
}
//public static <T> Collector toList()把元素收集到List集合中
//collect收集元素
//(Collectors.toList()创建集合,存入数据
List<Integer> collect = list.stream().collect(Collectors.toList());
System.out.println(collect);
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]
//public static <T> Collector toSet()把元素收集到Set集合中
//Set集合中不能添加重复元素
//Collectors.toSet()创建集合,存入数据
Set<Integer> collect1 = list1.stream().collect(Collectors.toSet());
System.out.println(collect1);
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
}
2. toMap 方法
案例
需求
创建ArrayList集合, 添加以下字符串数据, 键是姓名值是年龄
"zhangsan,23"
"lisi,24"
"wangwu,25"
筛选并保留大于等于24岁的人, 并将结果收集到Map集合中
public class Demo02 {
public static void main(String[] args) {
//创建ArrayList集合添加元素
ArrayList<String> list = new ArrayList<>();
list.add("张三,23");
list.add("李四,24");
list.add("王五,25");
//将输入放入流中
Map<String, Integer> map = list.stream().filter(
s -> {
//参数s就是每一个字符串元素,根据逗号切合
String[] arr = s.split(",");
//获取返回数组中,第二个元素就是年龄,转为int类型
int age = Integer.parseInt(arr[1]);
//返回判断结果
return age >= 24;
}
).collect(Collectors.toMap(
/*
List和Set是单列集合, 直接将数组整体存入即可, 所以toList和toSet都是是空参
Map集合是双列集合, 计算机不知道谁是键谁是值, 所以需要我们手动给出实现
Collectors.toMap(如果获取键 ,如果获取值); 将元素收集到Map集合中
*/
//参数1: s代表从流中依次获取的数据, 根据lambda表达式, 从s中切割出键即可
(String s) -> {
String name = s.split(",")[0];
return name;
},
//参数2: s代表从流中依次获取的数据, 根据lambda表达式, 从s中切割出值即可
(String s) -> {
int age = Integer.parseInt(s.split(",")[1]);
return age;
}
));
//打印map集合内容
System.out.println(map); //{李四=24, 王五=25}
}
}
2.4 Stream流的练习
现在有两个ArrayList集合, 分别存储6名男演员和6名女演员, 完成如下操作: 1. 男演员只要名字为三个字的前两人 -> filter name的长度==3 limit(2); 2. 女演员只要姓杨的, 并且不要第一个 -> filter startsWith("杨"); skip(1) 3. 将过滤后的男女演员合并到一起 -> concat(流1,流2); 4. 设计Actor类, 提供一个成员变量name, 带参构造方法, 以及对应的getXxx,setXxx方法 5. 将合并后流中的元素(字符串name), 作为构造参数创建演员对象并遍历(Actor对象只有一个属性name)
public class Demo {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("张国立");
list1.add("张晋");
list1.add("刘烨");
list1.add("郑伊健");
list1.add("徐峥");
list1.add("王宝强");
ArrayList<String> list2 = new ArrayList<>();
list2.add("郑爽");
list2.add("杨紫");
list2.add("关晓彤");
list2.add("张天爱");
list2.add("杨幂");
list2.add("赵丽颖");
//1. 男演员只要名字为三个字的前两人 -> filter name的长度==3 limit(2);
Stream<String> stream1 = list1.stream().filter(s -> s.length() == 3).limit(2);
//2. 女演员只要姓杨的, 并且不要第一个 -> filter startsWith("杨"); skip(1)
Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("杨")).skip(1);
//3. 将过滤后的男女演员合并到一起 -> concat(流1,流2);
Stream<String> stream3 = Stream.concat(stream1, stream2);
stream3.forEach(
name -> {
//5. 将合并后流中的元素(字符串name), 作为构造参数创建演员对象并遍历(Actor对象只有一个属性name)
Actor ac = new Actor(name);
System.out.println(ac);
/*
Actor{name='张国立'}
Actor{name='郑伊健'}
Actor{name='杨幂'}
*/
}
);
}
}
//4. 设计Actor类, 提供一个成员变量name, 带参构造方法, 以及对应的getXxx,setXxx方法
class Actor {
private String name;
public Actor() {
}
public Actor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Actor actor = (Actor) o;
return name != null ? name.equals(actor.name) : actor.name == null;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Actor{");
sb.append("name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
}
三、斗地主游戏
步骤:1.洗牌 2.发牌 3.看牌
洗牌是用Collections工具类中的shuffle方法,将集合中的元素打乱
发牌就是将每个玩家的牌和底牌找出来(利用索引)
看牌是打印玩家的牌和底牌
第一种方法: 发出来的牌是无序排列的
public class Demo02 {
public static void main(String[] args) {
//创建ArrayList集合
ArrayList<String> list = new ArrayList<>();
//创建放花色和数字的数组,
String[] color = {"♠", "♥", "♣", "♦"};
String[] number = {"2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "6", "5", "4", "3"};
//将大小王添加到集合的开头
list.add("大王");
list.add("小王");
//外层循环遍历数字,内层循环遍历花色,将曙色和数字添加到list集合中
for (String numbers : number) {
for (String colors : color) {
list.add(colors + numbers);
}
}
//该方法将集合中的元素打乱顺序
Collections.shuffle(list);
//创建存放玩家和底牌的TreeSet集合
TreeSet<String>play1 = new TreeSet<>();
TreeSet<String>play2 = new TreeSet<>();
TreeSet<String>play3 = new TreeSet<>();
TreeSet<String>dipai = new TreeSet<>();
//遍历装有花色和数字的list集合,分别将花色和数字添加到每个玩家和底牌的集合中
for (int i = 0; i < list.size(); i++) {
//最后三种牌放到底牌的集合中
if(i>=50){
dipai.add(list.get(i));
}
else if(i<50) {
if (i % 3 == 0) {
play1.add(list.get(i));
} else if (i % 3 == 1) {
play2.add(list.get(i));
} else if (i % 3 == 2) {
play3.add(list.get(i));
}
}
}
//打印每个玩家和底牌的集合元素
System.out.println(play1);
System.out.println(play2);
System.out.println(play3);
System.out.println(dipai);
}
}
第二种方法: 发出来的牌可有序排列
public class Demo {
public static void main(String[] args) {
//1.创建HashMap集合,里面存储索引值、牌的花色和数字
HashMap<Integer, String> map = new HashMap<>();
//2.创建ArrayList集合,里面存储索引值,类型为Integer
ArrayList<Integer> list = new ArrayList<>();
//3.创建两个数组分别存储牌的花色和数字
String[] color = {"♠", "♥", "♣", "♦"};
String[] number = {"2", "A", "K", "Q", "10", "J", "9", "8", "7", "6", "5", "4", "3"};
//4.定义索引从2开始,存储牌的花色和数字
int index = 2;
for (String numbers : number) {
for (String colors : color) {
map.put(index, numbers + colors);
list.add(index);
index++;
}
}
/*
//如果color在外层循环,number在内层循环,,则打印的时候不会按照牌的数字进行升序排列
//打印出来的牌会是无序的
for (String colors : color) {
for (String numbers : number) {
map.put(index, numbers + colors);
list.add(index);
index++;
}
}
*/
//将0索引和大王存在map集合中,将0和1索引添加到存储索引的list集合中
//这样每次发牌大王小王都会在前面
map.put(0, "大王");
list.add(0);
map.put(1, "小王");
list.add(1);
//利用Collections工具类中的shuffle方法,打乱list集合中的索引顺序
Collections.shuffle(list);
//创建四个存储玩家和底牌索引的TreeSet集合,类型为Integer
//因为TreeSet集合底层是自动升序排列的,所以使用
TreeSet<Integer> play01 = new TreeSet<>();
TreeSet<Integer> play02 = new TreeSet<>();
TreeSet<Integer> play03 = new TreeSet<>();
TreeSet<Integer> dipai = new TreeSet<>();
//遍历list索引集合,利用规律将索引添加到4个集合中
for (int i = 0; i < list.size(); i++) {
if (i <= 50) {
//玩家1的发牌规则
if (i % 3 == 0) {
//如果索引符合玩家1的发牌规则,则将这些索引添加到玩家1的集合中
play01.add(list.get(i));
//玩家2的发牌规则
} else if (i % 3 == 1) {
//如果索引符合玩家2的发牌规则,则将这些索引添加到玩家2的集合中
play02.add(list.get(i));
//玩家3的发牌规则
} else if (i % 3 == 2) {
//如果索引符合玩家3的发牌规则,则将这些索引添加到玩家3的集合中
play03.add(list.get(i));
}
//底牌的发牌规则
} else {
//剩余三张牌,将剩下的3个索引添加到底牌的集合中
dipai.add(list.get(i));
}
}
//将会打印每个玩家和底牌集合中的索引
method("玩家1", play01, map);
method("玩家2", play02, map);
method("玩家3", play03, map);
method("底牌", dipai, map);
}
//创建一个方法,将玩家姓名,玩家索引集合,存储所有花色和数字的map集合作为参数列表
public static void method(String s, TreeSet<Integer> name, HashMap<Integer, String> map) {
//遍历map集合
Set<Map.Entry<Integer, String>> entries = map.entrySet();
//打印玩家姓名
System.out.print(s + ":");
for (Map.Entry<Integer, String> entry : entries) {
//如果玩家的索引集合中包含map集合中的键(索引值)集合中,则将这个值(花色和数字)打印出来
if (name.contains(entry.getKey())) {
System.out.print(entry.getValue() + " ");
}
}
System.out.println();
}
}