ToB企服应用市场:ToB评测及商务社交产业平台
标题:
Java 8 Stream用法与常见题目息争决方式
[打印本页]
作者:
南飓风
时间:
2024-11-11 14:13
标题:
Java 8 Stream用法与常见题目息争决方式
1. 什么是 Stream API?
Stream API 是用于处理惩罚数据序列的功能,提供了一种高效、清晰和声明性的处理惩罚方式。Stream 不会存储数据,它是对数据源的高级抽象,可以举行聚合操作(如过滤、映射、排序、归约等)。Stream 的核心优势包括:
支持链式调用,增强代码可读性。
惰性求值特性,避免不必要的计算。
并行处理惩罚能力,充分利用多核 CPU 提拔性能。
2. Stream API 底子
Stream 可以通过多种方式创建,常见的有以下几种:
从 Collection 或 List 中获取。
使用 Stream.of() 方法。
使用 Arrays.stream() 处理惩罚数组。
使用文件或 I/O 操作天生。
示例:
List<String> stringList = Arrays.asList("Java", "Python", "C++", "JavaScript");
// 从集合获取 Stream
Stream<String> streamFromList = stringList.stream();
// 使用 Stream.of()
Stream<String> streamOfStrings = Stream.of("Hello", "World");
// 使用 Arrays.stream()
int[] intArray = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(intArray);
复制代码
3. Stream API 常见操作详解
Stream API 提供了丰富的操作来处理惩罚数据序列,这些操作分为中间操作和停止操作。中间操作不会立即执行,它们是惰性的,只有在停止操作触发时才会执行。停止操作会结束流的处理惩罚并天生结果。以下是对 Stream API 常见操作的细化讲解,包括分组操作等高级功能。
3.1. 中间操作
3.1.1. filter(Predicate predicate)
filter 用于根据条件过滤流中的元素。它接受一个 Predicate 接口,该接口界说了一个条件,返回值为布尔类型。满意条件的元素会保存在流中,不满意的会被过滤掉。
示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
// 输出 ["Alice"]
复制代码
3.1.2. map(Function mapper)
map 用于将流中的每个元素转换为另一种形式。它接受一个 Function 接口,将元素逐一映射为新的值。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// 输出 [5, 6, 6]
复制代码
3.1.3. flatMap(Function> mapper)
flatMap 用于将每个元素转换为一个流,然后将多个流合并为一个流。它适合处理惩罚嵌套的集合布局。
示例:
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("one", "two"),
Arrays.asList("three", "four")
);
List<String> flattenedList = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 输出 ["one", "two", "three", "four"]
复制代码
3.1.4. distinct()
distinct 用于去除流中重复的元素,保存唯一值。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 输出 [1, 2, 3, 4, 5]
复制代码
3.1.5. sorted() 和 sorted(Comparator comparator)
sorted 用于对流中的元素举行排序,默认是自然顺序。通过传入 Comparator 可以举行自界说排序。
示例:
List<String> names = Arrays.asList("John", "Jane", "Mark", "Emily");
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
// 输出 ["Emily", "Jane", "John", "Mark"]
List<String> reverseSortedNames = names.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// 输出 ["Mark", "John", "Jane", "Emily"]
复制代码
3.1.6. limit(long maxSize) 和 skip(long n)
limit 截取流中前 maxSize 个元素。
skip 跳过流中前 n 个元素。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// 输出 [1, 2, 3]
List<Integer> skippedNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
// 输出 [3, 4, 5, 6]
复制代码
3.2. 停止操作
3.2.1. collect(Collector collector)
collect 是用于将流中的元素收集到集合或其他数据布局中。常见的 Collector 工具包括 Collectors.toList()、Collectors.toSet()、Collectors.joining() 等。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry");
String joinedWords = words.stream()
.collect(Collectors.joining(", "));
// 输出 "apple, banana, cherry"
复制代码
3.2.2. forEach(Consumer action)
forEach 遍历流中的每个元素并执行给定的操作。
示例:
List<String> items = Arrays.asList("item1", "item2", "item3");
items.stream()
.forEach(System.out::println);
// 输出每个元素
复制代码
3.2.3. count()
count 返回流中元素的数量。
示例:
List<String> names = Arrays.asList("John", "Jane", "Mark");
long count = names.stream().count();
// 输出 3
复制代码
3.2.4. reduce(BinaryOperator accumulator)
reduce 用于将流中的元素逐一组合成一个结果。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
sum.ifPresent(System.out::println); // 输出 15
复制代码
3.2.5. findFirst() 和 findAny()
findFirst 返回流中第一个元素的 Optional。
findAny 返回流中任意一个元素的 Optional,通常用于并行流。
示例:
List<String> items = Arrays.asList("apple", "banana", "cherry");
Optional<String> firstItem = items.stream().findFirst();
firstItem.ifPresent(System.out::println); // 输出 "apple"
复制代码
3.2.6. allMatch(Predicate predicate)、anyMatch(Predicate predicate)、noneMatch(Predicate predicate)
allMatch 检查是否所有元素都满意给定条件。
anyMatch 检查是否有任一元素满意给定条件。
noneMatch 检查是否没有元素满意给定条件。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0); // false
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0); // true
复制代码
3.3. 分组操作
Collectors.groupingBy() 是 Stream API 中的一个高级操作,它允许根据某个属性对元素举行分组。返回的结果是一个 Map,其中键是分组的依据,值是分组后的列表。
示例:按字符串长度分组
List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
Map<Integer, List<String>> groupedByLength = words.stream()
.collect(Collectors.groupingBy(String::length));
groupedByLength.forEach((length, wordList) ->
System.out.println("长度为 " + length + " 的单词: " + wordList)
);
// 输出:
// 长度为 5 的单词: [apple]
// 长度为 6 的单词: [banana, cherry]
// 长度为 4 的单词: [date]
复制代码
示例:按员工部门分组
class Employee {
private String name;
private String department;
public Employee(String name, String department) {
this.name = name;
this.department = department;
}
public String getDepartment() {
return department;
}
public String getName() {
return name;
}
}
List<Employee> employees = Arrays.asList(
new Employee("Alice", "IT"),
new Employee("Bob", "HR"),
new Employee("Charlie", "IT"),
new Employee("David", "Finance")
);
Map<String, List<Employee>> employeesByDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
employeesByDepartment.forEach((department, empList) -> {
System.out.println("部门 " + department + ": " +
empList.stream().map(Employee::getName).collect(Collectors.joining(", ")));
});
// 输出:
// 部门 IT: Alice, Charlie
// 部门 HR: Bob
// 部门 Finance: David
复制代码
多级分组
Collectors.groupingBy() 支持多级分组,这在实际项目中非常有用。例如,按部门和职位举行分组:
Map<String, Map<String, List<Employee>>> multiLevelGrouping = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.groupingBy(Employee::getPosition)));
// 返回嵌套 Map 结构,按部门和职位分组
复制代码
通过这些详尽的用法和分组操作,开发者可以更轻松地处理惩罚复杂数据操作,编写清晰、高效、可维护的代码。
4. Stream API 高级操作
4.1. 并行流
Java 8 提供了并行流(parallelStream()),可以充分利用多核 CPU,提拔数据处理惩罚的性能。并行流将流中的任务分发到多个线程中并行处理惩罚。
List<Integer> largeList = IntStream.rangeClosed(1, 1000000)
.boxed()
.collect(Collectors.toList());
long start = System.currentTimeMillis();
largeList.parallelStream()
.filter(n -> n % 2 == 0)
.count();
long end = System.currentTimeMillis();
System.out.println("并行流处理时间: " + (end - start) + " ms");
复制代码
4.2. 惰性求值
Stream 的中间操作是惰性的,意味着它们在没有停止操作的情况下不会被执行。惰性求值有助于优化性能,淘汰不必要的计算。
Stream<String> lazyStream = Stream.of("one", "two", "three", "four")
.filter(s -> {
System.out.println("正在处理: " + s);
return s.length() > 3;
});
// 没有调用终止操作,filter 不会执行
System.out.println("未触发终止操作");
// 调用终止操作后,filter 才会被执行
lazyStream.forEach(System.out::println);
复制代码
5. 常见题目及解决方式
在使用 Java 8 Stream API 时,开发者大概会遇到一些常见的坑和题目。理解这些题目及其解决方案有助于编写结实的代码。以下是几个常见题目及其应对方法。
5.1. Collectors.toMap() 时 Key 重复题目
在使用 Collectors.toMap() 将流转换为 Map 时,假如流中的键重复,就会抛出 IllegalStateException,提示键重复。为了避免此题目,我们可以通过提供合并函数来处理惩罚重复的键。
题目示例:
List<String> items = Arrays.asList("apple", "banana", "apple", "orange");
Map<String, Integer> itemMap = items.stream()
.collect(Collectors.toMap(
item -> item,
item -> 1
));
// 这段代码会抛出 IllegalStateException,因为 "apple" 键重复
复制代码
解决方案:使用合并函数
通过提供一个合并函数来解决键重复题目,例如选择保存第一个值或累加值。
Map<String, Integer> itemMap = items.stream()
.collect(Collectors.toMap(
item -> item,
item -> 1,
(existingValue, newValue) -> existingValue + newValue // 合并函数
));
// 输出:{apple=2, banana=1, orange=1}
复制代码
**解释:**合并函数 (existingValue, newValue) -> existingValue + newValue 表现当键重复时,将值举行累加。
5.2. NullPointerException 题目
在使用 Stream API 举行操作时,假如流中存在 null 值,操作如 map()、filter() 等大概会抛出 NullPointerException。为了避免这种情况,通常必要在操作前举行空值检查。
题目示例:
List<String> words = Arrays.asList("apple", null, "banana", "cherry");
List<Integer> wordLengths = words.stream()
.map(String::length) // 如果遇到 null,会抛出 NullPointerException
.collect(Collectors.toList());
复制代码
解决方案:使用 filter() 过滤 null 值
List<Integer> wordLengths = words.stream()
.filter(Objects::nonNull) // 过滤掉 null 值
.map(String::length)
.collect(Collectors.toList());
// 输出 [5, 6, 6]
复制代码
5.3. ConcurrentModificationException 题目
当使用 Stream API 遍历集合并在迭代时修改集适时,会抛出 ConcurrentModificationException。这通常发生在对原始集合举行迭代并修改它的情况下。
题目示例:
List<String> names = new ArrayList<>(Arrays.asList("John", "Jane", "Mark", "Emily"));
names.stream().forEach(name -> {
if (name.equals("Mark")) {
names.remove(name); // 会抛出 ConcurrentModificationException
}
});
复制代码
解决方案:使用 removeIf() 方法或创建新的集合
// 使用 removeIf()
names.removeIf(name -> name.equals("Mark")); // 安全地删除元素
// 或者,使用流创建新的集合
List<String> filteredNames = names.stream()
.filter(name -> !name.equals("Mark"))
.collect(Collectors.toList());
复制代码
5.4. Stream 性能题目
虽然 Stream API 提供了优雅的语法,但在某些情况下会有性能题目,尤其是在使用 parallelStream() 时。并行流可以进步处理惩罚大量数据的性能,但在小数据集或 I/O 密集型操作中,大概会带来开销并导致性能降落。
建议:
在使用并行流前,分析数据规模和应用场景,确定是否有必要。
避免在 Stream 中举行复杂的同步操作或共享可变状态,以避免线程安全题目。
示例:
List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
.boxed()
.collect(Collectors.toList());
// 并行流
long start = System.currentTimeMillis();
numbers.parallelStream()
.filter(n -> n % 2 == 0)
.count();
long end = System.currentTimeMillis();
System.out.println("并行流处理时间: " + (end - start) + " ms");
// 顺序流
start = System.currentTimeMillis();
numbers.stream()
.filter(n -> n % 2 == 0)
.count();
end = System.currentTimeMillis();
System.out.println("顺序流处理时间: " + (end - start) + " ms");
复制代码
**注意:**在小数据集上,并行流的性能大概不如顺序流好。
5.5. 流的短路操作
Stream API 中有一些短路操作,可以淘汰不必要的处理惩罚,从而进步性能。
findFirst() 和 findAny()
:返回第一个或任意一个符合条件的元素,适用于必要快速找到结果的情况。
limit()
:截断流,适用于只必要处理惩罚部分数据时。
anyMatch()、allMatch()、noneMatch()
:检查流中是否有满意条件的元素,支持短路操作。
示例:使用短路操作优化性能
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
boolean hasEvenNumber = numbers.stream()
.anyMatch(n -> n % 2 == 0); // 一旦找到第一个偶数,就会终止遍历
System.out.println("是否存在偶数: " + hasEvenNumber); // 输出 true
复制代码
5.6. 数据并行性与共享可变状态
在使用并行流时,假如流中的元素共享了可变状态,大概会导致数据不一致或线程安全题目。
题目示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> results = new ArrayList<>();
numbers.parallelStream()
.forEach(results::add); // 可能会导致数据不一致
System.out.println(results);
复制代码
解决方案:
使用线程安全的集合,如 Collections.synchronizedList() 或 ConcurrentLinkedQueue,或者使用 collect() 收集结果。
List<Integer> results = numbers.parallelStream()
.collect(Collectors.toList()); // 推荐做法
复制代码
总结起来,Stream API 提供了强大的功能和简洁的语法,但在实际项目中使用时,必要了解和避免常见的陷阱和题目,以确保代码的安全性和性能。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4