目录

Java-Stream流

Java Stream流

Java Stream流

认识Stream流

  • 是Jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据
  • 优势:Stream流大量的结合了Lambda的语法风格来编程,功能强大、性能高效、代码简洁、可读性好

代码体验

package com.itheima.stream;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamDemo1 {
    public static void main(String[] args) {
        // 认识Stream流
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三");
        list.add("张翠兰");

        System.out.println(list);

        List<String> newList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("张") && name.length() == 2) {
                newList.add(name);
            }
        }
        System.out.println(newList);

        // 使用Stream流解决
        List<String> newList1 = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).collect(Collectors.toList()); // filter 过滤
        System.out.println(newList1);
    }
}

Stream流的使用步骤

准备数据源(集合、数组、…)→ 过滤 → 排序 → 去重→ … →获取结果

获取Stream流 ,Stream流代表着一条流水线,并能与数据源建立连接;

调用流水线各种方法 ,对数据进行处理;

获取处理的数据 ,遍历、统计、收集到一个新的集合中返回结果。

获取Stream流

  • 获取 **集合 ** 的Stream流
Collection 提供的如下方法说明
default Stream stream()获取当前集合对象的 Stream 流
  • 获取 **数组 ** 的Stream流
Arrays 类提供的如下方法说明
public static Stream stream(T[] array)获取当前数组的 Stream 流
Stream 类提供的如下方法说明
public static Stream of(T… values)获取当前接收数据的 Stream 流

代码实例

package com.itheima.stream;

import java.util.*;
import java.util.stream.Stream;

public class StreamDemo2 {
    public static void main(String[] args) {
        // 1.获取集合的Stream流
        Collection<String> list = new ArrayList<>();
        Stream<String> s = list.stream();

        // 2.获取Map集合的Stream流
        Map<String, Integer> map = new HashMap<>();
        // map.stream(); // 不能直接调用stream
        // 获取键流
        Stream<String> keyStream = map.keySet().stream();
        // 获取值流
        Stream<Integer> valueStream = map.values().stream();
        // 获取键值对流
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        // 获取数组的Stream流
        String[] name = {"张无忌", "赵敏", "张强", "张三丰", "张翠山", "张小强"};
        Stream<String> stream = Arrays.stream(name); // 方式1
        Stream<String> stream1 = Stream.of("张无忌", "赵敏"); // 方式2       
    }
}

Stream流提供的常用方法

  • 中间方法 指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。
Stream 提供的常用中间方法说明
Stream filter(Predicate<? super T> predicate)用于对流中的数据进行过滤
Stream sorted()对元素进行升序排序
Stream sorted(Comparator<? super I> comparator)按照指定规则排序
Stream limit(long maxSize)获取前几个元素
Stream skip(long n)跳过前几个元素
Stream distinct()去除流中重复的元素
Stream map(Function<? super I,? extends R> mapper)对元素进行加工,并返回对应的新流
static Stream concat(Stream a, Stream b)合并 a 和 b 两个流为一个流

代码实例

package com.itheima.stream;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamDemo3 {
    public static void main(String[] args) {
        // 掌握Stream流常用的中间方法
        // 1.对流中的数据进行过滤
        List<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三");
        list.add("张翠兰");
        list.add("张晓强");
        System.out.println(list);
        System.out.println("--------------------------对流中的数据进行过滤--------------------------");
        list.stream().filter(name -> name.startsWith("张") && name.length() == 3).forEach(System.out::println);
        // 2.对元素进行升序排序
        System.out.println("--------------------------对元素进行升序排序--------------------------");
        List<Double> list1 = new ArrayList<>();
        // 无规律添加成绩,有小数点
        list1.add(99.5);
        list1.add(88.9);
        list1.add(88.9);
        list1.add(76.9);
        list1.add(98.7);
        list1.add(65.4);
        list1.add(87.2);

        list1.stream().sorted((o1, o2) -> Double.compare(o1, o2)).forEach(System.out::println); // 升序排序
        // 3.对元素进行降序排序
        System.out.println("--------------------------对元素进行降序排序--------------------------");
        list1.stream().sorted((o1, o2) -> Double.compare(o2, o1)).forEach(System.out::println); // 降序排序
        // 4.获取前几个元素
        System.out.println("--------------------------获取前几个元素--------------------------");
        list.stream().limit(3).forEach(System.out::println);
        // 5.跳过前几个元素
        System.out.println("--------------------------跳过前几个元素--------------------------");
        list1.stream().skip(2).forEach(s -> System.out.println(s));
        // 6.去重
        System.out.println("--------------------------去重--------------------------");
        list1.stream().distinct().forEach(System.out::println);
        // 7.映射/加工方法,返回对应的新Stream流
        System.out.println("--------------------------映射/加工方法,返回对应的新Stream流--------------------------");
        // 每个学生加5分,加分超过100分的,则变为100分
        list1.stream().map(s -> (s + 5) > 100 ? 100 : s + 5).forEach(System.out::println);

        // 8.合并流
        System.out.println("--------------------------合并流--------------------------");
        Stream<Object> concat = Stream.concat(list.stream(), list1.stream());
        concat.forEach(System.out::println);
    }
}

终结方法、收集Stream流

Stream流的终结方法

  • 终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
Stream 提供的常用终结方法说明
void forEach(Consumer action)对此流运算后的元素执行遍历
long count()统计此流运算后的元素个数
Optional max(Comparator<? super I> comparator)获取此流运算后的最大值元素
Optional min(Comparator<? super I> comparator)获取此流运算后的最小值元素

收集Stream流

  • 收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回
    • 集合/数组:才是开发中的目的
    • Stream流:方便操作集合/数组的手段
Stream 提供的常用终结方法说明
R collect(Collector collector)把流处理后的结果收集到一个指定的集合中去
Object[] toArray()把流处理后的结果收集到一个数组中去
Collectors 工具类提供的具体收集方式说明
public static Collector toList()把元素收集到 List 集合中
public static Collector toSet()把元素收集到 Set 集合中
public static Collector toMap(Function keyMapper , Function valueMapper)把元素收集到 Map 集合中

代码实例

package com.itheima.stream;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Comparator.comparingDouble;

public class StreamDemo4 {
    public static void main(String[] args) {
        // Stream流的终结方法
        List<Teacher> teacher = new ArrayList<>();
        teacher.add(new Teacher("张三丰", 50, 51000));
        teacher.add(new Teacher("张无忌", 20, 20000));
        teacher.add(new Teacher("周芷若", 18, 18000));
        teacher.add(new Teacher("赵敏", 16, 16000));
        teacher.add(new Teacher("金毛狮王", 60, 33000));

        System.out.println(teacher);
        // Stream的终结方法
        // 1.forEach
        System.out.println("------------------------------forEach终结方法------------------------------");
        teacher.stream().filter(t -> t.getAge() > 30 && t.getSalary() > 30000).forEach(System.out::println);

        // 2.count
        System.out.println("------------------------------count终结方法------------------------------");
        System.out.println(teacher.stream().filter(t -> t.getAge() > 30 && t.getSalary() > 30000).count());

        // 3.max
        System.out.println("------------------------------max终结方法------------------------------");
        Optional<Teacher> max = teacher.stream().max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
        // 简化代码
        Optional<Teacher> max1 = teacher.stream().max(comparingDouble(Teacher::getSalary));
        Teacher maxTeacher = max.get();
        System.out.println(maxTeacher);

        // 4.min
        System.out.println("------------------------------min终结方法------------------------------");
        Optional<Teacher> min = teacher.stream().min((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
        Teacher minTeacher = min.get();
        System.out.println(minTeacher);


        // Stream的收集方法,Collectors 工具类提供的具体收集方式
        System.out.println("------------------------------collectors收集方法------------------------------");
        List<String> name = new ArrayList<>();
        name.add("张三丰");
        name.add("张无忌");
        name.add("张无忌");
        name.add("周芷若");
        name.add("赵敏");
        name.add("张强");
        name.add("张三");
        name.add("张翠兰");
        name.add("张晓强");

        // 收集到List集合
        System.out.println("--------------------------收集到List集合--------------------------");
        Stream<String> s1 = name.stream().filter(n -> n.startsWith("张") && n.length() == 3);
        List<String> s1List = s1.collect(Collectors.toList());
        System.out.println(s1List);

        // 收集到Set集合
        System.out.println("--------------------------收集到Set集合--------------------------");
        HashSet<Object> s1Set = new HashSet<>();
        s1Set.addAll(s1List);
        System.out.println(s1Set);

        // 收集到Map集合
        System.out.println("--------------------------收集到Map集合--------------------------");
        Stream<Teacher> s2 = teacher.stream().filter(t -> t.getAge() > 15);
        Map<String, Double> map = s2.collect(Collectors.toMap(Teacher::getName, Teacher::getSalary));
        System.out.println(map);

        // 收集到数组
        System.out.println("--------------------------收集到数组--------------------------");
        Stream<String> s3 = name.stream().filter(n -> n.startsWith("张") && n.length() == 3);
        Object[] array = s3.toArray();
        System.out.println(Arrays.toString(array));
    }
}

方法中的可变参数(拓展)

  • 就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型…参数名称;
  • 可变参数的特点和好处
    • 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
    • 好处:常常用来灵活的接收数据。
  • 可变参数的注意事项:
    • 可变参数在方法内部就是一个数组
    • 一个形参列表中可变参数只能有一个
    • 可变参数必须放在形参列表的最后面

Collections工具类

  • Collections ——一个用来操作集合的工具类
  • Collections提供的常用静态方法
方法名称说明
public static boolean addAll(Collection<? super T> c, T… elements)给集合批量添加元素
public static void shuffle(List list)打乱 List 集合中的元素顺序
public static void sort(List list)对 List 集合中的元素进行升序排序
public static void sort(List list, Comparator<? super T> c)对 List 集合中元素,按照比较器对象指定的规则进行排序

案例——斗地主游戏设计

  • 业务需求:
    • 总共有54张牌
    • 点数:“3” “4” “5” “6” “7” “8” “9” “10” “J” “Q” “K” “A” “2”
    • 花色:4种花色
    • 大小王
    • 斗地主:发出51张牌,剩下3张做为底牌
  • 分析实现
  • 在启动游戏房间的时候,应该提前准备好54张牌接着,需要完成洗牌、发牌、对牌排序、看牌

代码实例

启动游戏

package com.itheima.stream;

public class Game {
    public static void main(String[] args) {
        // 开发斗地主游戏
        // 每张牌都以一个对象,定义Card类
        // 每个游戏房间都是一个对象,定义一个GameRoom类
        new GameRoom().start();

    }
}

创建一个Card类,用于封装Card对象的特征

package com.itheima.stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor

public class Card {
    // 定义牌的号码size和牌的花色
    private String size;
    private String color;
    private int num;

    @Override
    public String toString() {
        return size + color;
    }
}

创建一个游戏Room

  • 完成牌的初始化,洗牌,发牌,留出底牌,看牌,给任意玩家底牌,给牌排序等功能
package com.itheima.stream;

import java.util.*;

public class GameRoom {
    // 1.准备牌,定义一个集合,存储54张牌
    private List<Card> allCards = new ArrayList<>();

    //2.初始化54张牌,用实例代码块(示例代码块和对象一起初始化)
    {
        String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
        String[] colors = {"♥", "♦", "♣", "♠"};
        // 两个循环进行牌的匹配
        int num = 0;
        for (String size : sizes) {
            num++;
            for (String color : colors) {
                // 创建牌对象,并添加到集合中
                Card card = new Card(size, color, num);
                // 添加到allCards集合中
                allCards.add(card);
            }
        }
        // 创建大小王并添加到集合中去
        Collections.addAll(allCards, new Card("", "🃏", ++num), new Card("", "👲", ++num));
        // 查看新牌
        System.out.println("原始牌:" + allCards);
    }

    public void start() {
        // 3.洗牌,洗牌就是打乱集合中的顺序
        Collections.shuffle(allCards);
        System.out.println("洗牌后:" + allCards);

        // 4.发牌,定义三个玩家player:老王、老李、老朱,三个玩家对应着三个集合,因此使用Map集合,玩家是键,牌的集合是值
        Map<String, List<Card>> players = new HashMap<>();
        List<Card> lw = new ArrayList<>();
        players.put("老王", lw);
        List<Card> ll = new ArrayList<>();
        players.put("老李", ll);
        List<Card> lz = new ArrayList<>();
        players.put("老朱", lz);

        for (int i = 0; i < allCards.size() - 3; i++) {
            if (i % 3 == 0) {
                lw.add(allCards.get(i));
            }else if(i % 3 == 1) {
                ll.add(allCards.get(i));
            }else {
                lz.add(allCards.get(i));
            }
        }

        // 5.看牌,遍历Map集合
        for (Map.Entry<String, List<Card>> entry : players.entrySet()){
            // 获取玩家姓名
            String key = entry.getKey();
            // 获取牌的集合
            List<Card> value = entry.getValue();
            System.out.println(key + "的牌为:" + value);
        }
        // 底牌
        // subList(int fromIndex, int toIndex) 是 List 接口提供的一个方法,用于截取列表中的一部分元素,返回一个新的子列表
        List<Card> lastCard = allCards.subList(allCards.size() - 3, allCards.size());
        System.out.println("底牌:" + lastCard);

        // 6.随机选一个玩家将底牌交给玩家
        String key = players.keySet().toArray()[new Random().nextInt(players.size())].toString();
        players.get(key).addAll(lastCard);

        // 7.给牌排序(增加一个排序属性)
        sortCard(lw);
        System.out.println("老王的牌:" + lw);
        sortCard(ll);
        System.out.println("老李的牌:" + ll);
        sortCard(lz);
        System.out.println("老朱的牌:" + lz);

    }

    private void sortCard(List<Card> cards) {
        // 牌排序
        Collections.sort(cards, new Comparator<Card>() {
            @Override
            public int compare(Card o1, Card o2) {
                // 牌大小排序
                return o2.getNum() - o1.getNum();
            }
        });
    }

}