Day19
可变参数
可变参数:就是形参的个数是可以变化的
- 格式:修饰符 返回值类型 方法名(数据类型… 变量名){}
- 范例:public static int sum(int… a){}
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
示例:
public class ChangeableTest {
public static void main(String[] args) {
//调用方法传递数组中的值
int sum = getSum(1,2,3,4,5);
System.out.println("sum = "+sum); //sum = 15
}
//创建一个求和方法,以数组为可变参数
public static int getSum(int... arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
}
创建不可变的集合
方法名 | 说明 |
---|---|
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K,V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
示例:
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class BubianTest {
public static void main(String[] args) {
//集合的批量添加
//首先通过调用List.of方法来创建一个不可变的集合,of方法的形参就是一个可变参数
//再创建一个ArrayList集合,并把这个不可变的集合中所有的数据,都添加到ArrayList中
ArrayList<String> list = new ArrayList<>(List.of("a","b","c","d"));
System.out.println(list);
//set集合传递的参数不能重复
Set<String> set = Set.of("a","b","c","d");
System.out.println(set);
Map<String, String> map = Map.of("三三", "陕西", "东东", "沈阳", "西西", "西安");
System.out.println(map);
Map<String,String> map1 = Map.ofEntries(Map.entry("三三","陕西"),
Map.entry("东东","沈阳"),
Map.entry("西西","西安"));
System.out.println(map1);
}
}
总结
- 在List、Set、Map接口中,都存在of方法,创建一个不可变的集合;
- 这个集合不能添加、不能删除、不能修改;
- 但是可以结合集合的带参构造,实现集合的批量添加;
- 在Map接口中,还有一个ofEntries方法可以提高代码的阅读性;
- 首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合中。
Stream流
Stream流的三类方法
- 获取Stream流;创建一条流水线,并把数据放到流水线上准备进行操作。
- 中间方法;流水线上的操作,一次操作完毕之后,还可以继续进行其他操作。
- 终结方法;一个Stream流只能有一个终结方法,是流水线上的最后一个操作。
Stream流的获取方法
- 单列集合
- 可以使用Collection接口中的默认方法stream()生成流
- default Stream stream()
- 双列集合
- 间接的生成流
- 可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流
- 数组
- Arrays中的静态方法stream生成流
- 同种数据类型的多个数据
- 使用Stream.of(T…values)生成流
示例:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Stream;
public class MyStream {
public static void main(String[] args) {
//单列集合
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//Stream<String> stream = list.stream();
//stream.forEach(s -> System.out.println(s));
list.stream().forEach(s -> System.out.println(s));
//双列集合
HashMap<String,Integer> hm = new HashMap<>();
hm.put("三三",21);
hm.put("小四",22);
hm.put("小花",19);
hm.put("水娃",17);
//双列集合不能直接获取stream流
//keySet,先获取到所有的键,再把这个Set集合中所有的键放到Stream流中
hm.keySet().stream().forEach(s -> System.out.println(s));
//entrySet,先获取到所有的键值对对象
//再把这个Set集合中所有的键值对对象放到Stream流中
hm.entrySet().stream().forEach(s -> System.out.println(s));
//数组
int[] arr = {1,2,3,4,5};
Arrays.stream(arr).forEach(s -> System.out.println(s));
//同种数据类型的多个数据
Stream.of(1,2,3,4,5,6,7,8,9).forEach(s -> System.out.println(s));
}
}
总结:
- 单列集合:集合对象.stream();
- 双列集合:不能直接获取,需要间接获取,集合对象.keySet().stream(); 集合对象.entrySet().stream();
- 数组:Arrarys.stream(数组名);
- 同种数据类型的多个数据:Stream.of(数据1,数据2,数据3…);
Stream流的常见中间操作方法
- Stream filter(Predicate predicate):用于对流中的数据进行过滤
- predicate接口中的方法
- boolean test(T t):对给定的参数进行判断,返回一个布尔值
- Stream limit(long maxSize):截取指定参数个数的数据
- Stream skip(long n):跳过指定参数个数的数据
- static Stream concat(Stream a,Stream b):合并a和b两个流为一个流
- Stream distinct():去除流中重复的元素,依赖(hashCode和equals方法)
import java.util.ArrayList;
import java.util.stream.Stream;
/*
过滤出张开头的字符串
*/
public class MyStream2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张冠李戴");
list.add("张启山");
list.add("张牙舞爪");
list.add("水涨船高");
list.add("张灯结彩");
list.add("蒲苇纫如丝");
list.add("磐石无转移");
//filter方法中获取流中的每一个数据
//而test方法中的s,就依次表示流中的数据
//我们只要在test方法中对s进行判断就可以了
//如果判断的结果为true,则当前数据留下
//如果判断的结果为false,则当前数据就不要
// list.stream().filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// boolean result = s.startsWith("张");
// return result;
// }
// }).forEach(s -> System.out.println(s));
//因为Predicate接口中只有一个抽象方法test
//所以我们可以使用Lambda表达式来简化
// list.stream().filter(
// (String s) -> {
// boolean result = s.startsWith("张");
// return result;
// }
// ).forEach(s -> System.out.println(s));
//简化Lambda表达式得
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
System.out.println("----------------");
//Stream<T> limit(long maxSize):截取指定参数个数的数据
list.stream().limit(2).forEach(s -> System.out.println(s));
System.out.println("----------------");
//Stream<T> skip(long n):跳过指定参数个数的数据
list.stream().skip(2).forEach(s -> System.out.println(s));
System.out.println("----------------");
//static <T> Stream<T> concat(Stream a,Stream b):合并a和b两个流为一个流
ArrayList<String> list2 = new ArrayList<>();
list2.add("张冠李戴");
list2.add("张启山");
list2.add("张牙舞爪");
list2.add("水涨船高");
list2.add("张灯结彩");
list2.add("蒲苇纫如丝");
list2.add("磐石无转移");
// Stream<String> stream1 = list.stream();
// Stream<String> stream2 = list2.stream();
// Stream<String> stream3 = Stream.concat(stream1, stream2);
// stream3.forEach(s -> System.out.println(s));
Stream.concat(list.stream(),list2.stream()).forEach(s -> System.out.println(s));
System.out.println("----------------");
//Stream<T> distinct():去除流中重复的元素,依赖(hashCode和equals方法)
list.add("张牙舞爪");
list.stream().distinct().forEach(s -> System.out.println(s));
}
}
Stream流的常见终结操作方法
- void forEach(Consumer action):对此流的每个元素执行操作
- Consumer接口中的方法
- void accept(T t):对给定的参数执行此操作
- long count:返回此流中的元素数
import java.util.ArrayList;
public class MyStream3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张冠李戴");
list.add("张启山");
list.add("张牙舞爪");
list.add("水涨船高");
list.add("张灯结彩");
list.add("蒲苇纫如丝");
list.add("磐石无转移");
//在forEach方法中的底层,会循环获取到流中的每一个数据
//并循环调用accept方法,并把每一个数据传递给accept方法
//s就依次表示了流中的每一个数据
//所以,我们只要在accept方法中,写上处理的业务逻辑就可以了
// list.stream().forEach(
// new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// }
// );
// //Lambda表达式简化
// list.stream().forEach(
// (String s) -> {
// System.out.println(s);
// }
// );
//进一步简化
list.stream().forEach(s -> System.out.println(s));
//long count:返回此流中的元素数
long count = list.stream().count();
System.out.println(count);
}
}
运行结果:
Stream流的收集操作
**注:**在Stream流中无法直接修改集合,数组等数据源中的数据。
Stream流的收集方法
- R collect(Collector collector)
工具类Collectors提供了具体的收集方式
- public static Collector toList():把元素收集到List集合中(元素可重复)
- public static Collector toSet():把元素收集到Set集合中(元素不可重复)
- public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
- public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/*
定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
将集合中的奇数删除,只保留偶数
遍历集合得到2,4,6,8,10
*/
public class MyStream4 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list1.add(i);
}
//filter负责过滤数据
//collect负责收集数据,
// 获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中
//Collectors.toList():在底层会创建一个List集合,并把所有的数据添加到List集合中
List<Integer> list = list1.stream().filter(number -> number % 2 == 0).collect(Collectors.toList());
System.out.println(list);
Set<Integer> set = list1.stream().filter(number -> number % 2 == 0).collect(Collectors.toSet());
System.out.println(set);
}
}
运行结果:
案例
现在有两个ArrayList集合,分别存储6名男角色和六名女角色,要求完成如下操作
- 男角色只要名字为三个字的前两人
- 女角色只要姓杨的,并且不要第一个
- 把过滤后的男角色姓名和女角色姓名合并到一起
- 把上一步操作后的元素作为构造方法的参数创建角色对象,遍历数据
- 角色类Role,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
import java.util.ArrayList;
import java.util.stream.Stream;
public class RoleTest {
public static void main(String[] args) {
ArrayList<String> manList = new ArrayList<>();
manList.add("夏侯惇");
manList.add("关羽");
manList.add("赵云");
manList.add("诸葛亮");
manList.add("刘备");
manList.add("司马懿");
ArrayList<String> womanList = new ArrayList<>();
womanList.add("蔡文姬");
womanList.add("杨玉环");
womanList.add("黄月英");
womanList.add("施夷光");
womanList.add("杨蓉");
womanList.add("武曌");
//男角色只要名字为三个字的前两人
Stream<String> man = manList.stream().filter(name -> name.length() == 3).limit(2);
//女角色只要姓杨的,并且不要第一个
Stream<String> woman = womanList.stream().filter(name -> name.startsWith("杨")).skip(1);
//把过滤后的男角色姓名和女角色姓名合并到一起
Stream.concat(man, woman).forEach(name ->{
Role role = new Role(name);
System.out.println(role);
});
}
}
运行结果:
练习1
定义一个方法,可以求出任意个整数的和.然后在主方法中调用.
public class Lian1 {
public static void main(String[] args) {
int i1 = add(3,2);
int i2 = add(5,4,3);
int i3 = add(6,7,8,9);
System.out.println("两个整数的和是:"+i1);
System.out.println("三个整数的和是:"+i2);
System.out.println("四个整数的和是:"+i3);
}
public static int add(int... a){
//此时的可变参数a可以当成数组使用
int sum = 0;
for(int i : a){
sum += i;
}
return sum;
}
}
运行结果:
练习2
随机生成10个10至20之间的随机数(数字允许重复),使用Stream流的技术,找出大于15的元素并打印出来;
import java.util.ArrayList;
import java.util.Random;
public class Lian2 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Random r = new Random();
for (int i = 1; i <= 10; i++) {
int sj = r.nextInt(11)+10;
System.out.println("第"+i+"次生成的随机数是:"+sj);
list.add(sj);
}
//获取流对象,筛选数据并遍历输出
System.out.println("大于15的元素有:");
list.stream().filter(y->y>15).forEach(y-> System.out.print(y+" "));
}
}
运行结果:
练习3
键盘录入3个学生信息,存储到学生对象(姓名,年龄).然后添加到ArrayList集合中;键盘录入3个居住地信息,添加到另一个集合ArrayList;把两个list集合中的数据收集到同一个map集合中,键是学生对对象,值是居住地址;要求map集合中不能存在相同的学生信息.并按照学生年龄降序排列;使用Stream流输出集合中所有姓张的人信息;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.TreeMap;
public class StudentTest {
public static void main(String[] args) {
//键盘录入3个学生信息,存储到学生对象(姓名,年龄).然后添加到ArrayList集合中
Scanner sc = new Scanner(System.in);
ArrayList<Student> list = new ArrayList<>();
for(int i = 1; i <= 3; i++){
System.out.println("请输入第"+i+"个学生姓名:");
String name = sc.next();
System.out.println("请输入第"+i+"个学生年龄:");
int age = sc.nextInt();
Student st = new Student(name,age);
list.add(st);
}
//键盘录入3个居住地信息,添加到另一个集合ArrayList
ArrayList<String> listAddr = new ArrayList<>();
for(int i = 1; i <= 3; i++){
System.out.println("请输入第"+i+"个学生居住地:");
String addr = sc.next();
listAddr.add(addr);
}
//把两个list集合中的数据收集到同一个map集合中,键是学生对对象,值是居住地址
//要求map集合中不能存在相同的学生信息.并按照学生年龄降序排列
TreeMap<Student,String> tm = new TreeMap<>((s1,s2)->s2.getAge()-s1.getAge());
for(int i = 0; i < list.size(); i++){
Student key = list.get(i);
String value = listAddr.get(i);
tm.put(key,value);
}
//使用Stream流输出集合中所有姓张的人信息
tm.entrySet().stream().filter(s->s.getKey().getName().startsWith("张")).forEach(s-> System.out.println("学生信息:"+s.getKey()+",对应的居住地址是:"+s.getValue()));
}
}
运行结果: