一、Collections工具类
各位同学,前面我们已经把Collection家族的集合都学习完了。为了更加方便的对Collection集合进行操作,今天我们还要学习一个操作Collection集合的工具类,叫做Collections。但是Collections工具类中需要用到一个没有学过的小知识点,叫做可变参数,所以必须先学习这个前置知识可变参数,再学习Collections工具类,最后再利用这个工具类做一个综合案例。
1.1 可变参数
首先,我们来学习一下可变参数。关于可变参数我们首先要知道它是什么,然后要知道它的本质。搞清楚这两个问题,可变参数就算你学明白了。
- 可变参数是一种特殊的形式参数,定义在方法、构造器的形参列表处,它可以让方法接收多个同类型的实际参数。
- 可变参数在方法内部,本质上是一个数组
接下来,我们编写代码来演示一下
public class ParamTest{ public static void main(String[] args){ //不传递参数,下面的nums长度则为0, 打印元素是[] test(); //传递3个参数,下面的nums长度为3,打印元素是[10, 20, 30] test(10,20,30); //传递一个数组,下面数组长度为4,打印元素是[10,20,30,40] int[] arr = new int[]{10,20,30,40} test(arr); } public static void test(int...nums){ //可变参数在方法内部,本质上是一个数组 System.out.println(nums.length); System.out.println(Arrays.toString(nums)); System.out.println("----------------"); } }
最后还有一些错误写法,需要让大家写代码时注意一下,不要这么写哦!!!
- 一个形参列表中,只能有一个可变参数;否则会报错
- 一个形参列表中如果多个参数,可变参数需要写在最后;否则会报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUnjG8sm-1690457662704)(assets/1667194652653.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWyzAf13-1690457662706)(assets/1667194696892.png)]
1.2 Collections工具类
有了可变参数的基础,我们再学习Collections这个工具类就好理解了,因为这个工具类的方法中会用到可变参数。
注意Collections并不是集合,它比Collection多了一个s,一般后缀为s的类很多都是工具类。这里的Collections是用来操作Collection的工具类。它提供了一些好用的静态方法,如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QylsMsKj-1690457662707)(assets/1667195108724.png)]
我们把这些方法用代码来演示一下:
public class CollectionsTest{ public static void main(String[] args){ //1.public static <T> boolean addAll(Collection<? super T> c, T...e) List<String> names = new ArrayList<>(); Collections.addAll(names, "张三","王五","李四", "张麻子"); System.out.println(names); //2.public static void shuffle(List<?> list):对集合打乱顺序 Collections.shuffle(names); System.out.println(names); //3.public static <T> void short(List<T list): 对List集合排序 List<Integer> list = new ArrayList<>(); list.add(3); list.add(5); list.add(2); Collections.sort(list); System.out.println(list); } }
上面我们往集合中存储的元素要么是Stirng类型,要么是Integer类型,他们本来就有一种自然顺序所以可以直接排序。但是如果我们往List集合中存储Student对象,这个时候想要对List集合进行排序自定义比较规则的。指定排序规则有两种方式,如下:
排序方式1:让元素实现Comparable接口,重写compareTo方法
比如现在想要往集合中存储Studdent对象,首先需要准备一个Student类,实现Comparable接口。
public class Student implements Comparable<Student>{ private String name; private int age; private double height; //排序时:底层会自动调用此方法,this和o表示需要比较的两个对象 @Override public int compareTo(Student o){ //需求:按照年龄升序排序 //如果返回正数:说明左边对象的年龄>右边对象的年龄 //如果返回负数:说明左边对象的年龄<右边对象的年龄, //如果返回0:说明左边对象的年龄和右边对象的年龄相同 return this.age - o.age; } //...getter、setter、constructor.. }
然后再使用Collections.sort(list集合)
对List集合排序,如下:
//3.public static <T> void short(List<T list): 对List集合排序 List<Student> students = new ArrayList<>(); students.add(new Student("蜘蛛精",23,169.7)); students.add(new Student("紫霞",22,169.8)); students.add(new Student("紫霞",22,169.8)); students.add(new Student("至尊宝",26,169.5)); /* 原理:sort方法底层会遍历students集合中的每一个元素,采用排序算法,将任意两个元素两两比较; 每次比较时,会用一个Student对象调用compareTo方法和另一个Student对象进行比较; 根据compareTo方法返回的结果是正数、负数,零来决定谁大,谁小,谁相等,重新排序元素的位置 注意:这些都是sort方法底层自动完成的,想要完全理解,必须要懂排序算法才行; */ Collections.sort(students); System.out.println(students);
排序方式2:使用调用sort方法是,传递比较器
/* 原理:sort方法底层会遍历students集合中的每一个元素,采用排序算法,将任意两个元素两两比较; 每次比较,会将比较的两个元素传递给Comparator比较器对象的compare方法的两个参数o1和o2, 根据compare方法的返回结果是正数,负数,或者0来决定谁大,谁小,谁相等,重新排序元素的位置 注意:这些都是sort方法底层自动完成的,不需要我们完全理解,想要理解它必须要懂排序算法才行. */ Collections.sort(students, new Comparator<Student>(){ @Override public int compare(Student o1, Student o2){ return o1.getAge()-o2.getAge(); } }); System.out.println(students);
1.3 斗地主案例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-efM7QA1E-1690457662709)(assets/1667306432458.png)]
我们先分析一下业务需求:
- 总共有54张牌,每一张牌有花色和点数两个属性、为了排序还可以再加一个序号
- 点数可以是:
“3”,"4","5","6","7","8","9","10","J","Q","K","A","2"
- 花色可以是:
“♣”,"♠","♥","♦"
- 斗地主时:三个玩家没人手里17张牌,剩余3张牌作为底牌
第一步:为了表示每一张牌有哪些属性,首先应该新建一个扑克牌的类 第二步:启动游戏时,就应该提前准备好54张牌 第三步:接着再完全洗牌、发牌、捋牌、看牌的业务逻辑
先来完成第一步,定义一个扑克类Card
public class Card { private String number; private String color; // 每张牌是存在大小的。 private int size; // 0 1 2 .... public Card() { } public Card(String number, String color, int size) { this.number = number; this.color = color; this.size = size; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } @Override public String toString() { return color + number ; } }
再完成第二步,定义一个房间类,初始化房间时准备好54张牌
public class Room { // 必须有一副牌。 private List<Card> allCards = new ArrayList<>(); public Room(){ // 1、做出54张牌,存入到集合allCards // a、点数:个数确定了,类型确定。 String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"}; // b、花色:个数确定了,类型确定。 String[] colors = {"♠", "♥", "♣", "♦"}; int size = 0; // 表示每张牌的大小 // c、遍历点数,再遍历花色,组织牌 for (String number : numbers) { // number = "3" size++; // 1 2 .... for (String color : colors) { // 得到一张牌 Card c = new Card(number, color, size); allCards.add(c); // 存入了牌 } } // 单独存入小大王的。 Card c1 = new Card("", "🃏" , ++size); Card c2 = new Card("", "👲" , ++size); Collections.addAll(allCards, c1, c2); System.out.println("新牌:" + allCards); } }
最后完成第三步,定义一个启动游戏的方法,完成洗牌、发牌、捋牌、看牌的业务逻辑
/** * 游戏启动 */ public void start() { // 1、洗牌: allCards Collections.shuffle(allCards); System.out.println("洗牌后:" + allCards); // 2、发牌,首先肯定要定义 三个玩家。 List(ArrayList) Set(TreeSet) List<Card> linHuChong = new ArrayList<>(); List<Card> jiuMoZhi = new ArrayList<>(); List<Card> renYingYing = new ArrayList<>(); // 正式发牌给这三个玩家,依次发出51张牌,剩余3张做为底牌。 // allCards = [♥3, ♣10, ♣4, ♥K, ♦Q, ♣2, 🃏, ♣8, .... // 0 1 2 3 4 5 6 ... % 3 for (int i = 0; i < allCards.size() - 3; i++) { Card c = allCards.get(i); // 判断牌发给谁 if(i % 3 == 0){ // 请啊冲接牌 linHuChong.add(c); }else if(i % 3 == 1){ // 请啊鸠来接牌 jiuMoZhi.add(c); }else if(i % 3 == 2){ // 请盈盈接牌 renYingYing.add(c); } } // 3、对3个玩家的牌进行排序 sortCards(linHuChong); sortCards(jiuMoZhi); sortCards(renYingYing); // 4、看牌 System.out.println("啊冲:" + linHuChong); System.out.println("啊鸠:" + jiuMoZhi); System.out.println("盈盈:" + renYingYing); List<Card> lastThreeCards = allCards.subList(allCards.size() - 3, allCards.size()); // 51 52 53 System.out.println("底牌:" + lastThreeCards); jiuMoZhi.addAll(lastThreeCards); sortCards(jiuMoZhi); System.out.println("啊鸠抢到地主后:" + jiuMoZhi); } /** * 集中进行排序 * @param cards */ private void sortCards(List<Card> cards) { Collections.sort(cards, new Comparator<Card>() { @Override public int compare(Card o1, Card o2) { // return o1.getSize() - o2.getSize(); // 升序排序 return o2.getSize() - o1.getSize(); // 降序排序 } }); }
不要忘记了写测试类了,
public class GameDemo { public static void main(String[] args) { // 1、牌类。 // 2、房间 Room m = new Room(); // 3、启动游戏 m.start(); } }
二、Map集合
2.1 Map概述体系
各位同学,前面我们已经把单列集合学习完了,接下来我们要学习的是双列集合。首先我们还是先认识一下什么是双列集合。
所谓双列集合,就是说集合中的元素是一对一对的。Map集合中的每一个元素是以key=value
的形式存在的,一个key=value
就称之为一个键值对,而且在Java中有一个类叫Entry类,Entry的对象用来表示键值对对象。
所有的Map集合有如下的特点:键不能重复,值可以重复,每一个键只能找到自己对应的值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvbWmMzG-1690457662726)(assets/1667308368751.png)]
下面我们先写一个Map集合,保存几个键值对,体验一下Map集合的特点
public class MapTest1 { public static void main(String[] args) { // Map<String, Integer> map = new HashMap<>(); // 一行经典代码。 按照键 无序,不重复,无索引。 Map<String, Integer> map = new LinkedHashMap<>(); // 有序,不重复,无索引。 map.put("手表", 100); map.put("手表", 220); // 后面重复的数据会覆盖前面的数据(键) map.put("手机", 2); map.put("Java", 2); map.put(null, null); System.out.println(map); Map<Integer, String> map1 = new TreeMap<>(); // 可排序,不重复,无索引 map1.put(23, "Java"); map1.put(23, "MySQL"); map1.put(19, "李四"); map1.put(20, "王五"); System.out.println(map1); } }
Map集合也有很多种,在Java中使用不同的类来表示的,每一种Map集合其键的特点是有些差异的,值是键的一个附属值,所以我们只关注键的特点就可以了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ss5dwUPu-1690457662727)(assets/1667308506610.png)]
关于Map集合是什么,以及Map集合的体系我们先了解到这里,接下来就具体学习一下Map集合的通用方法。
2.2 Map集合的常用方法
各位同学,上节课我们已经认识了Map集合,接下来我们学习一下Map集合提供了那些方法供我们使用。由于Map是所有双列集合的父接口,所以我们只需要学习Map接口中每一个方法是什么含义,那么所有的Map集合方法你就都会用了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zycGFJyV-1690457662728)(assets/1667308854001.png)]
public class MapTest2 { public static void main(String[] args) { // 1.添加元素: 无序,不重复,无索引。 Map<String, Integer> map = new HashMap<>(); map.put("手表", 100); map.put("手表", 220); map.put("手机", 2); map.put("Java", 2); map.put(null, null); System.out.println(map); // map = {null=null, 手表=220, Java=2, 手机=2} // 2.public int size():获取集合的大小 System.out.println(map.size()); // 3、public void clear():清空集合 //map.clear(); //System.out.println(map); // 4.public boolean isEmpty(): 判断集合是否为空,为空返回true ,反之! System.out.println(map.isEmpty()); // 5.public V get(Object key):根据键获取对应值 int v1 = map.get("手表"); System.out.println(v1); System.out.println(map.get("手机")); // 2 System.out.println(map.get("张三")); // null // 6. public V remove(Object key):根据键删除整个元素(删除键会返回键的值) System.out.println(map.remove("手表")); System.out.println(map); // 7.public boolean containsKey(Object key): 判断是否包含某个键 ,包含返回true ,反之 System.out.println(map.containsKey("手表")); // false System.out.println(map.containsKey("手机")); // true System.out.println(map.containsKey("java")); // false System.out.println(map.containsKey("Java")); // true // 8.public boolean containsValue(Object value): 判断是否包含某个值。 System.out.println(map.containsValue(2)); // true System.out.println(map.containsValue("2")); // false // 9.public Set<K> keySet(): 获取Map集合的全部键。 Set<String> keys = map.keySet(); System.out.println(keys); // 10.public Collection<V> values(); 获取Map集合的全部值。 Collection<Integer> values = map.values(); System.out.println(values); // 11.把其他Map集合的数据倒入到自己集合中来。(拓展) Map<String, Integer> map1 = new HashMap<>(); map1.put("java1", 10); map1.put("java2", 20); Map<String, Integer> map2 = new HashMap<>(); map2.put("java3", 10); map2.put("java2", 222); map1.putAll(map2); // putAll:把map2集合中的元素全部倒入一份到map1集合中去。 System.out.println(map1); System.out.println(map2); } }
2.3 Map集合遍历方式1
Map集合一共有三种遍历方式,我们先来学习第一种,他需要用到下面的两个方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0H6t1JOx-1690457662730)(assets/1667308962740.png)]
/** * 目标:掌握Map集合的遍历方式1:键找值 */ public class MapTest1 { public static void main(String[] args) { // 准备一个Map集合。 Map<String, Double> map = new HashMap<>(); map.put("蜘蛛精", 162.5); map.put("蜘蛛精", 169.8); map.put("紫霞", 165.8); map.put("至尊宝", 169.5); map.put("牛魔王", 183.6); System.out.println(map); // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8} // 1、获取Map集合的全部键 Set<String> keys = map.keySet(); // System.out.println(keys); // [蜘蛛精, 牛魔王, 至尊宝, 紫霞] // key // 2、遍历全部的键,根据键获取其对应的值 for (String key : keys) { // 根据键获取对应的值 double value = map.get(key); System.out.println(key + "=====>" + value); } } }
2.4 Map集合遍历方式2
各位同学,接下来我们学习Map集合的第二种遍历方式,这种遍历方式更加符合面向对象的思维。
前面我们给大家介绍过,Map集合是用来存储键值对的,而每一个键值对实际上是一个Entry对象。
这里Map集合的第二种方式,是直接获取每一个Entry对象,把Entry存储扫Set集合中去,再通过Entry对象获取键和值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WH2fsP8e-1690457662733)(assets/1667309587178.png)]
/** * 目标:掌握Map集合的第二种遍历方式:键值对。 */ public class MapTest2 { public static void main(String[] args) { Map<String, Double> map = new HashMap<>(); map.put("蜘蛛精", 169.8); map.put("紫霞", 165.8); map.put("至尊宝", 169.5); map.put("牛魔王", 183.6); System.out.println(map); // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8} // entries = [(蜘蛛精=169.8), (牛魔王=183.6), (至尊宝=169.5), (紫霞=165.8)] // entry = (蜘蛛精=169.8) // entry = (牛魔王=183.6) // ... // 1、调用Map集合提供entrySet方法,把Map集合转换成键值对类型的Set集合 Set<Map.Entry<String, Double>> entries = map.entrySet(); for (Map.Entry<String, Double> entry : entries) { String key = entry.getKey(); double value = entry.getValue(); System.out.println(key + "---->" + value); } } }
2.5 Map集合遍历方式3
Map集合的第三种遍历方式,需要用到下面的一个方法forEach,而这个方法是JDK8版本以后才有的。调用起来非常简单,最好是结合的lambda表达式一起使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i25MR6Px-1690457662734)(assets/1667309230571.png)]
/** * 目标:掌握Map集合的第二种遍历方式:键值对。 */ public class MapTest3 { public static void main(String[] args) { Map<String, Double> map = new HashMap<>(); map.put("蜘蛛精", 169.8); map.put("紫霞", 165.8); map.put("至尊宝", 169.5); map.put("牛魔王", 183.6); System.out.println(map); // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8} //遍历map集合,传递匿名内部类 map.forEach(new BiConsumer<String, Double>() { @Override public void accept(String k, Double v) { System.out.println(k + "---->" + v); } }); //遍历map集合,传递Lambda表达式 map.forEach(( k, v) -> { System.out.println(k + "---->" + v); }); } }
2.6 Map集合案例
学习完Map集合的基本用法之后,接下来我们做一个综合案例,将Map集合运用一下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-87YisJvC-1690457662737)(assets/1667311182716.png)]
先分析需求,再考虑怎么用代码实现
1.首先可以将80个学生选择的景点放到一个集合中去(也就是说,集合中的元素是80个任意的ABCD元素) 2.准备一个Map集合用来存储景点,以及景点被选择的次数 3.遍历80个学生选择景点的集合,得到每一个景点,判断Map集合中是否包含该景点 如果不包含,则存储"景点=1" 如果包含,则存获取该景点原先的值,再存储"景点=原来的值+1"; 此时新值会覆盖旧值
/** * 目标:完成Map集合的案例:统计投票人数。 */ public class MapDemo4 { public static void main(String[] args) { // 1、把80个学生选择的景点数据拿到程序中来。 List<String> data = new ArrayList<>(); String[] selects = {"A", "B", "C", "D"}; Random r = new Random(); for (int i = 1; i <= 80; i++) { // 每次模拟一个学生选择一个景点,存入到集合中去。 int index = r.nextInt(4); // 0 1 2 3 data.add(selects[index]); } System.out.println(data); // 2、开始统计每个景点的投票人数 // 准备一个Map集合用于统计最终的结果 Map<String, Integer> result = new HashMap<>(); // 3、开始遍历80个景点数据 for (String s : data) { // 问问Map集合中是否存在该景点 if(result.containsKey(s)){ // 说明这个景点之前统计过。其值+1. 存入到Map集合中去 result.put(s, result.get(s) + 1); }else { // 说明这个景点是第一次统计,存入"景点=1" result.put(s, 1); } } System.out.println(result); } }
三、Stream流(补充)
各位同学,接下来我们学习一个全新的知识,叫做Stream流(也叫Stream API)。它是从JDK8以后才有的一个新特性,是专业用于对集合或者数组进行便捷操作的。有多方便呢?我们用一个案例体验一下,然后再详细学习。
3.1 Stream流体验
案例需求:有一个List集合,元素有"张三丰","张无忌","周芷若","赵敏","张强"
,找出姓张,且是3个字的名字,存入到一个新集合中去。
List<String> names = new ArrayList<>(); Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强"); System.out.println(names);
- 用传统方式来做,代码是这样的
// 找出姓张,且是3个字的名字,存入到一个新集合中去。 List<String> list = new ArrayList<>(); for (String name : names) { if(name.startsWith("张") && name.length() == 3){ list.add(name); } } System.out.println(list);
- 用Stream流来做,代码是这样的(ps: 是不是想流水线一样,一句话就写完了)
List<String> list2 = names.stream().filter(s -> s.startsWith("张")).filter(a -> a.length()==3).collect(Collectors.toList()); System.out.println(list2);
先不用知道这里面每一句话是什么意思,具体每一句话的含义,待会再一步步学习。现在只是体验一下。
学习Stream流我们接下来,会按照下面的步骤来学习。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjNJTwXy-1690457662739)(assets/1667649164429.png)]
3.2 Stream流的创建
好,接下来我们正式来学习Stream流。先来学习如何创建Stream流、或者叫获取Stream流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4MyFrBop-1690457662740)(assets/1667649331568.png)]
主要掌握下面四点: 1、如何获取List集合的Stream流? 2、如何获取Set集合的Stream流? 3、如何获取Map集合的Stream流? 4、如何获取数组的Stream流?
直接上代码演示
/** * 目标:掌握Stream流的创建。 */ public class StreamTest2 { public static void main(String[] args) { // 1、如何获取List集合的Stream流? List<String> names = new ArrayList<>(); Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强"); Stream<String> stream = names.stream(); // 2、如何获取Set集合的Stream流? Set<String> set = new HashSet<>(); Collections.addAll(set, "刘德华","张曼玉","蜘蛛精","马德","德玛西亚"); Stream<String> stream1 = set.stream(); stream1.filter(s -> s.contains("德")).forEach(s -> System.out.println(s)); // 3、如何获取Map集合的Stream流? Map<String, Double> map = new HashMap<>(); map.put("古力娜扎", 172.3); map.put("迪丽热巴", 168.3); map.put("马尔扎哈", 166.3); map.put("卡尔扎巴", 168.3); Set<String> keys = map.keySet(); Stream<String> ks = keys.stream(); Collection<Double> values = map.values(); Stream<Double> vs = values.stream(); Set<Map.Entry<String, Double>> entries = map.entrySet(); Stream<Map.Entry<String, Double>> kvs = entries.stream(); kvs.filter(e -> e.getKey().contains("巴")) .forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue())); // 4、如何获取数组的Stream流? String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"}; Stream<String> s1 = Arrays.stream(names2); Stream<String> s2 = Stream.of(names2); } }
3.3 Stream流中间方法
在上一节,我们学习了创建Stream流的方法。接下来我们再来学习,Stream流中间操作的方法。
中间方法指的是:调用完方法之后其结果是一个新的Stream流,于是可以继续调用方法,这样一来就可以支持链式编程(或者叫流式编程)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T1omtUAw-1690457662742)(assets/1667649379223.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtciJk6U-1690457662744)(assets/1667649509262.png)]
话不多说,直接上代码演示
/** * 目标:掌握Stream流提供的常见中间方法。 */ public class StreamTest3 { public static void main(String[] args) { List<Double> scores = new ArrayList<>(); Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0); // 需求1:找出成绩大于等于60分的数据,并升序后,再输出。 scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s)); List<Student> students = new ArrayList<>(); Student s1 = new Student("蜘蛛精", 26, 172.5); Student s2 = new Student("蜘蛛精", 26, 172.5); Student s3 = new Student("紫霞", 23, 167.6); Student s4 = new Student("白晶晶", 25, 169.0); Student s5 = new Student("牛魔王", 35, 183.3); Student s6 = new Student("牛夫人", 34, 168.5); Collections.addAll(students, s1, s2, s3, s4, s5, s6); // 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出. students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30) .sorted((o1, o2) -> o2.getAge() - o1.getAge()) .forEach(s -> System.out.println(s)); // 需求3:取出身高最高的前3名学生,并输出。 students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight())) .limit(3).forEach(System.out::println); System.out.println("-----------------------------------------------"); // 需求4:取出身高倒数的2名学生,并输出。 s1 s2 s3 s4 s5 s6 students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight())) .skip(students.size() - 2).forEach(System.out::println); // 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。 students.stream().filter(s -> s.getHeight() > 168).map(Student::getName) .distinct().forEach(System.out::println); // distinct去重复,自定义类型的对象(希望内容一样就认为重复,重写hashCode,equals) students.stream().filter(s -> s.getHeight() > 168) .distinct().forEach(System.out::println); Stream<String> st1 = Stream.of("张三", "李四"); Stream<String> st2 = Stream.of("张三2", "李四2", "王五"); Stream<String> allSt = Stream.concat(st1, st2); allSt.forEach(System.out::println); } }
3.5 Stream流终结方法
最后,我们再学习Stream流的终结方法。这些方法的特点是,调用完方法之后,其结果就不再是Stream流了,所以不支持链式编程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qpUUJlCS-1690457662746)(assets/1667649788535.png)]
我列举了下面的几个终结方法,接下来用几个案例来一个一个给同学们演示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wxHiuZnz-1690457662748)(assets/1667649867150.png)]
话不多说,直接上代码
/** * 目标:Stream流的终结方法 */ public class StreamTest4 { public static void main(String[] args) { List<Student> students = new ArrayList<>(); Student s1 = new Student("蜘蛛精", 26, 172.5); Student s2 = new Student("蜘蛛精", 26, 172.5); Student s3 = new Student("紫霞", 23, 167.6); Student s4 = new Student("白晶晶", 25, 169.0); Student s5 = new Student("牛魔王", 35, 183.3); Student s6 = new Student("牛夫人", 34, 168.5); Collections.addAll(students, s1, s2, s3, s4, s5, s6); // 需求1:请计算出身高超过168的学生有几人。 long size = students.stream().filter(s -> s.getHeight() > 168).count(); System.out.println(size); // 需求2:请找出身高最高的学生对象,并输出。 Student s = students.stream().max((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get(); System.out.println(s); // 需求3:请找出身高最矮的学生对象,并输出。 Student ss = students.stream().min((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get(); System.out.println(ss); // 需求4:请找出身高超过170的学生对象,并放到一个新集合中去返回。 // 流只能收集一次。 List<Student> students1 = students.stream().filter(a -> a.getHeight() > 170).collect(Collectors.toList()); System.out.println(students1); Set<Student> students2 = students.stream().filter(a -> a.getHeight() > 170).collect(Collectors.toSet()); System.out.println(students2); // 需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高,存入到一个Map集合返回。 Map<String, Double> map = students.stream().filter(a -> a.getHeight() > 170) .distinct().collect(Collectors.toMap(a -> a.getName(), a -> a.getHeight())); System.out.println(map); // Object[] arr = students.stream().filter(a -> a.getHeight() > 170).toArray(); Student[] arr = students.stream().filter(a -> a.getHeight() > 170).toArray(len -> new Student[len]); System.out.println(Arrays.toString(arr)); } }
到这里,关于Stream流的操常见操作我们就已经学习完了。当然Stream流还有一些其他的方法,同学们遇到了也可以自己再研究一下。