Java 流:初学者指南
你好,未来的Java大师们!今天,我们将踏上一段激动人心的旅程,探索Java流的世界。如果你是编程新手,不用担心——我会成为你的友好向导,我们会一步步来。在本教程结束时,你将能够像专业人士一样处理数据流!
Java中的流是什么?
想象你在一个传送带寿司餐厅。寿司盘不断地在你面前移动,你可以挑选你想要的。这在Java中基本上就是流的含义——它是一个元素序列,你可以逐个处理,而无需一次性将它们全部存储在内存中。
流在Java 8中被引入,以简化数据处理,并使其更高效。它们允许我们以更声明式的方式对数据进行操作,告诉Java我们想做什么,而不是如何做。
在Java中生成流
让我们从创建我们的第一个流开始。有几种方式可以做到这一点,但我们将从一个简单的例子开始:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
// 从列表创建流
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
Stream<String> fruitStream = fruits.stream();
// 直接创建流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
}
}
在这个例子中,我们创建了两个流。第一个,fruitStream
,是从水果列表中创建的。第二个,numberStream
,是使用Stream.of()
方法直接创建的。
常见的流操作
现在我们有了流,让我们看看可以在它们上面执行的一些常见操作。
forEach 方法
forEach
方法就像一个友好的机器人,它会遍历流中的每个元素并对它们进行操作。让我们用它来打印我们的水果:
fruits.stream().forEach(fruit -> System.out.println(fruit));
这将打印:
apple
banana
cherry
date
在这里,fruit -> System.out.println(fruit)
被称为lambda表达式。它是告诉Java对每个元素执行什么操作的简写方式。
map 方法
map
方法就像一根魔杖,可以转换流中的每个元素。让我们用它将所有水果转换为大写:
List<String> upperCaseFruits = fruits.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseFruits);
这将打印:
[APPLE, BANANA, CHERRY, DATE]
String::toUpperCase
是一个方法引用,这是Java的另一个便捷特性,就像说“在每个字符串上使用toUpperCase方法”。
filter 方法
filter
方法就像夜店的保镖,只让某些元素通过。让我们用它来只保留以'a'开头的水果:
List<String> aFruits = fruits.stream()
.filter(fruit -> fruit.startsWith("a"))
.collect(Collectors.toList());
System.out.println(aFruits);
这将打印:
[apple]
limit 方法
limit
方法就像说“我只想要这么多,谢谢!”它限制了流中的元素数量:
List<String> firstTwoFruits = fruits.stream()
.limit(2)
.collect(Collectors.toList());
System.out.println(firstTwoFruits);
这将打印:
[apple, banana]
sorted 方法
sorted
方法就像整理你的书架。它将元素按顺序排列:
List<String> sortedFruits = fruits.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedFruits);
这将打印:
[apple, banana, cherry, date]
并行处理
流的一个很酷的特性是它们可以很容易地并行处理,这可能会加快对大型数据集的操作。你可以这样创建一个并行流:
fruits.parallelStream().forEach(fruit -> System.out.println(fruit + " " + Thread.currentThread().getName()));
这可能会打印出类似以下内容:
banana ForkJoinPool.commonPool-worker-1
apple main
date ForkJoinPool.commonPool-worker-2
cherry ForkJoinPool.commonPool-worker-3
每次运行时,顺序可能会不同,因为水果是在并行处理的!
收集器
收集器是特殊的工具,帮助我们收集流操作的结果。我们在示例中使用了collect(Collectors.toList())
来将我们的流结果转换回列表。还有许多其他有用的收集器:
// 将元素连接成一个字符串
String fruitString = fruits.stream().collect(Collectors.joining(", "));
System.out.println(fruitString); // 打印: apple, banana, cherry, date
// 计算元素数量
long fruitCount = fruits.stream().collect(Collectors.counting());
System.out.println(fruitCount); // 打印: 4
// 对元素进行分组
Map<Character, List<String>> fruitGroups = fruits.stream()
.collect(Collectors.groupingBy(fruit -> fruit.charAt(0)));
System.out.println(fruitGroups); // 打印: {a=[apple], b=[banana], c=[cherry], d=[date]}
统计
流还可以帮助我们计算数值数据的统计信息:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());
这将打印:
Count: 5
Sum: 15
Min: 1
Max: 5
Average: 3.0
Java 流示例
让我们用一个更复杂的例子来把所有东西放在一起。假设我们有一个包含学生年龄和成绩的列表:
class Student {
String name;
int age;
double grade;
Student(String name, int age, double grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}
public class StreamExample {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 22, 90.5),
new Student("Bob", 20, 85.0),
new Student("Charlie", 21, 92.3),
new Student("David", 23, 88.7)
);
// 获取年龄超过21岁的学生的平均成绩
double averageGrade = students.stream()
.filter(student -> student.age > 21)
.mapToDouble(student -> student.grade)
.average()
.orElse(0.0);
System.out.println("Age over 21 average grade: " + averageGrade);
// 获取成绩前2名的学生姓名
List<String> topStudents = students.stream()
.sorted((s1, s2) -> Double.compare(s2.grade, s1.grade))
.limit(2)
.map(student -> student.name)
.collect(Collectors.toList());
System.out.println("Top 2 students: " + topStudents);
}
}
这个例子展示了我们如何将多个流操作链在一起,以执行复杂的数据处理任务,同时保持代码简洁易读。
就这样,伙计们!你已经迈出了进入Java流世界的第一步。记住,熟能生巧,所以不要害怕尝试这些概念。在你意识到之前,你将能够像专业人士一样处理数据流!快乐编码!
Credits: Image by storyset