Stream
1.类的方法列表
创建该类Stream
是为了便于构建数据流链。为此,该类Stream<T>
具有返回新Stream
对象的方法。
这些数据流中的每一个都执行一个简单的操作,但是如果您将它们组合成链并添加有趣的lambda 函数,那么您将拥有一种强大的机制来生成您想要的输出。很快你就会亲眼看到。
以下是该类的方法Stream
(仅是最基本的方法):
方法 | 描述 |
---|---|
|
从一组对象创建一个流 |
|
根据指定规则生成流 |
|
连接两个流 |
|
过滤数据,只传递符合指定规则的数据 |
|
删除重复项。不传递已经遇到的数据 |
|
对数据进行排序 |
|
对流中的每个元素执行操作 |
|
返回一个被截断的流,使其不超过指定的限制 |
|
跳过前 n 个元素 |
|
将数据从一种类型转换为另一种类型 |
|
将数据从一种类型转换为另一种类型 |
|
检查流中是否至少有一个元素匹配指定的规则 |
|
检查流中的所有元素是否都匹配指定的规则 |
|
检查流中是否没有元素匹配指定的规则 |
|
返回找到的第一个与规则匹配的元素 |
|
返回流中与规则匹配的任何元素 |
|
搜索数据流中的最小元素 |
|
返回数据流中的最大元素 |
|
返回数据流中元素的数量 |
|
从流中读取所有数据并将其作为集合返回 |
Stream
2.班级的中间和终端操作
如您所见,并非上表中的所有方法都返回一个Stream
. Stream
这与类的方法可以分为中间(也称为非终结)方法和终结方法有关。
中间方法
中间方法返回一个实现接口的对象Stream
,它们可以链接在一起。
终端方法
终端方法返回 a 以外的值Stream
。
方法调用管道
因此,您可以构建一个由任意数量的中间方法和最后一个终端方法调用组成的流管道。这种方法可以让您实现相当复杂的逻辑,同时提高代码的可读性。
数据流中的数据根本不会改变。中间方法链是一种灵活的(声明性的)方式,用于指定将在调用终端方法后执行的数据处理管道。
也就是说,如果终端方法没有被调用,那么数据流中的数据就不会被任何方式处理。只有调用终端方法后,数据才开始按照流管道中指定的规则进行处理。
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
中间方法和终端方法的比较:
中间的 | 终端 | |
---|---|---|
返回类型 | Stream |
不是Stream |
可以与同类型的多个方法组合形成一个流水线 | 是的 | 不 |
单个管道中的方法数 | 任何 | 不超过一个 |
产生最终结果 | 不 | 是的 |
开始处理流中的数据 | 不 | 是的 |
让我们看一个例子。
假设我们有一个动物爱好者俱乐部。明天俱乐部将庆祝姜猫日。俱乐部有宠物主人,每个人都有一份宠物清单。它们不仅限于猫。
任务:你需要找出所有姜猫的名字,为明天的“职业假期”制作个性化的贺卡。贺卡应按猫的年龄从大到小排序。
首先,我们提供一些类来帮助解决这个任务:
public enum Color {
WHITE,
BLACK,
DARK_GREY,
LIGHT_GREY,
FOXY,
GREEN,
YELLOW,
BLUE,
MAGENTA
}
public abstract class Animal {
private String name;
private Color color;
private int age;
public Animal(String name, Color color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
public int getAge() {
return age;
}
}
public class Cat extends Animal {
public Cat(String name, Color color, int age) {
super(name, color, age);
}
}
public class Dog extends Animal {
public Dog(String name, Color color, int age) {
super(name, color, age);
}
}
public class Parrot extends Animal {
public Parrot(String name, Color color, int age) {
super(name, color, age);
}
}
public class Pig extends Animal {
public Pig(String name, Color color, int age) {
super(name, color, age);
}
}
public class Snake extends Animal {
public Snake(String name, Color color, int age) {
super(name, color, age);
}
}
public class Owner {
private String name;
private List<Animal> pets = new ArrayList<>();
public Owner(String name) {
this.name = name;
}
public List<Animal> getPets() {
return pets;
}
}
现在让我们看一下Selector
类,其中将根据指定的标准进行选择:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Selector {
private static List<Owner> owners;
private static void initData() {
final Owner owner1 = new Owner("Ronan Turner");
owner1.getPets().addAll(List.of(
new Cat("Baron", Color.BLACK, 3),
new Cat("Sultan", Color.DARK_GREY, 4),
new Dog("Elsa", Color.WHITE, 0)
));
final Owner owner2 = new Owner("Scarlet Murray");
owner2.getPets().addAll(List.of(
new Cat("Ginger", Color.FOXY, 7),
new Cat("Oscar", Color.FOXY, 5),
new Parrot("Admiral", Color.BLUE, 3)
));
final Owner owner3 = new Owner("Felicity Mason");
owner3.getPets().addAll(List.of(
new Dog("Arnold", Color.FOXY, 3),
new Pig("Vacuum Cleaner", Color.LIGHT_GREY, 8)
));
final Owner owner4 = new Owner("Mitchell Stone");
owner4.getPets().addAll(List.of(
new Snake("Mr. Boa", Color.DARK_GREY, 2)
));
final Owner owner5 = new Owner("Jonathan Snyder");
owner5.getPets().addAll(List.of(
new Cat("Fisher", Color.BLACK, 16),
new Cat("Zorro", Color.FOXY, 14),
new Cat("Margo", Color.WHITE, 3),
new Cat("Brawler", Color.DARK_GREY, 1)
));
owners = List.of(owner1, owner2, owner3, owner4, owner5);
}
}
剩下的就是向main
方法中添加代码。目前,我们首先调用该initData()
方法,该方法填充俱乐部中的宠物主人列表。然后我们选择按年龄降序排列的姜猫的名字。
首先,让我们看一下不使用流来解决这个任务的代码:
public static void main(String[] args) {
initData();
List<String> findNames = new ArrayList<>();
List<Cat> findCats = new ArrayList<>();
for (Owner owner : owners) {
for (Animal pet : owner.getPets()) {
if (Cat.class.equals(pet.getClass()) && Color.FOXY == pet.getColor()) {
findCats.add((Cat) pet);
}
}
}
Collections.sort(findCats, new Comparator<Cat>() {
public int compare(Cat o1, Cat o2) {
return o2.getAge() - o1.getAge();
}
});
for (Cat cat : findCats) {
findNames.add(cat.getName());
}
findNames.forEach(System.out::println);
}
现在让我们看看另一种选择:
public static void main(String[] args) {
initData();
final List<String> findNames = owners.stream()
.flatMap(owner -> owner.getPets().stream())
.filter(pet -> Cat.class.equals(pet.getClass()))
.filter(cat -> Color.FOXY == cat.getColor())
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.map(Animal::getName)
.collect(Collectors.toList());
findNames.forEach(System.out::println);
}
如您所见,代码更加紧凑。此外,流管道的每一行都是一个动作,因此它们可以像英文句子一样阅读:
|
从一个移动Stream<Owner> 到一个Stream<Pet> |
|
只保留数据流中的猫 |
|
只保留数据流中的姜猫 |
|
按年龄降序排列 |
|
获取名称 |
|
将结果放入列表 |
3.创建流
该类Stream
具有三个我们尚未介绍的方法。这三个方法的目的是创建新的线程。
Stream<T>.of(T obj)
方法
该of()
方法创建一个由单个元素组成的流。Stream<T>
当一个函数将一个对象作为参数,但你只有一个对象时,通常需要这样做T
。然后您可以轻松简单地使用该of()
方法来获取由单个元素组成的流。
例子:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
方法
该of()
方法创建一个由传递的元素组成的流。允许任意数量的元素。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
方法
该generate()
方法允许您设置一个规则,该规则将用于在请求时生成流的下一个元素。例如,您可以每次给出一个随机数。
例子:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
方法
该concat()
方法将两个传递的流连接成一个。读取数据时,首先从第一个流读取,然后从第二个流读取。例子:
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.of(10, 11, 12, 13, 14);
Stream<Integer> result = Stream.concat(stream1, stream2);
4.过滤数据
另外 6 种方法创建新的数据流,让您将流组合成不同复杂度的链(或管道)。
Stream<T> filter(Predicate<T>)
方法
此方法返回一个新的数据流,该数据流根据传递的规则过滤源数据流。必须在类型为 的对象上调用该方法。Stream<T>
您可以使用lambda 函数指定过滤规则,然后编译器会将其转换为Predicate<T>
对象。
例子:
链式流 | 解释 |
---|---|
|
只保留小于三的数字 |
|
只保留大于零的数字 |
Stream<T> sorted(Comparator<T>)
方法
此方法返回一个新的数据流,它对源流中的数据进行排序。您传入一个comparator,它设置用于比较数据流的两个元素的规则。
Stream<T> distinct()
方法
此方法返回一个新的数据流,它只包含源数据流中的唯一元素。丢弃所有重复数据。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.distinct(); // 1, 2, 3, 4, 5
Stream<T> peek(Consumer<T>)
方法
此方法返回一个新的数据流,尽管其中的数据与源流中的数据相同。但是当从流中请求下一个元素时,您传递给该方法的函数peek()
将被调用。
如果将函数传递System.out::println
给peek()
方法,则所有对象在通过流时都会显示出来。
Stream<T> limit(int n)
方法
此方法返回一个新的数据流,它只包含源数据流中的第一个n
元素。丢弃所有其他数据。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3
Stream<T> skip(int n)
方法
此方法返回一个新的数据流,其中包含与源流相同的所有元素,但跳过(忽略)第一个n
元素。例子:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.skip(3); // 4, 5, 2, 2, 2, 3, 4
GO TO FULL VERSION