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();
}
});
}
}