一、自己的理解 函数式编程
从学习JAVA到现在一直都是面向对象的编程思想,一直以来的编程思想都是将相同类型的对象抽象出来形成类,但是函数式编程的思想和这种思想刚好相反,它是将方法抽象出来形成统一接口,输入的方式得到了本质的变化,是不是理解起来还是有点抽象,那么请看下图:
传统编程思想
方法不变,输入不同值得到不同结果。
函数式编程思想
值不变,输入不同方法得到不同结果。
二、Lambda表达式
Lambda表达式是Java 8引入的一种语法特性,它可以简化函数式编程的代码书写。
格式
@Test
public void test() throws Exception {
Runnable runnable = () -> { System.out.println("thread success"); };
}
()
:表示该Lambda表达式没有参数。在这个例子中,Lambda表达式不接受任何参数。->
:箭头符号,将参数列表和Lambda表达式的主体分隔开。{}
:花括号内的代码块,表示Lambda表达式的具体逻辑。在这个例子中,Lambda表达式的主体是打印"thread success"。
条件
Lambda表达式只能用于函数式接口(Functional Interface)或者SAM(Single Abstract Method 单个抽象方法)类型。函数式接口是指只有一个抽象方法的接口,可以通过`@FunctionalInterface`注解进行标识。Runnable就是函数式接口。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
应用
1、作为函数式接口的实例
Lambda表达式可以作为函数式接口的实例,函数式接口是只有一个抽象方法的接口。通过Lambda表达式,可以直接实现函数式接口的抽象方法,而不需要显式地编写实现类。
Runnable runnable = () -> {
// 执行具体逻辑
};
2、作为方法的参数
Lambda表达式可以作为方法的参数进行传递。这样可以在调用方法时,直接在参数位置上使用Lambda表达式来定义方法的具体逻辑。
public void doSomething(Function<String, Integer> function) {
// 执行具体逻辑
}
doSomething((String s) -> s.length());
3、作为方法的返回值
Lambda表达式可以作为方法的返回值进行返回。这样可以在方法内部使用Lambda表达式来定义方法的具体逻辑,并将其返回给调用者。
public Function<String, Integer> createFunction() {
return (String s) -> s.length();
}
三、Lambda表达书中的双冒号
语法
list.forEach(System.out::println);
应用
静态方法引用
可以引用一个类的静态方法。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(System.out::println);
实例方法引用
可以引用一个特定对象的实例方法。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(String::toUpperCase);
构造函数引用
可以引用一个类的构造函数。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Person> persons = numbers.stream().map(Person::new).collect(Collectors.toList());
四、Stream流的用法
1 filter(Predicate<T> predicate)
根据给定的条件过滤出符合条件的元素并返回一个新的Stream。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
.filter(num -> num % 2 == 0)
.collect(Collectors.toList());
// 过滤出偶数 [2, 4]
Predicate<T>
函数式接口,可以理解为是一个返回值为Boolean类型的方法,T为输入类型。
2 map(Function<T, R> mapper)
将每个元素通过给定的映射函数转换为另一种类型,并返回一个新的Stream。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 转换为名字的长度 [5, 3, 7]
Function<T, R>
函数式接口,可以理解为是一个转化方法,T为输入,R为输出。
3 flatMap(Function<T, Stream<R>> mapper)
将每个元素通过给定的映射函数转换为Stream并将多个Stream合并为一个Stream。
List<List<Integer>> numbers = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));
List<Integer> flattenedNumbers = numbers.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 合并成一个列表 [1, 2, 3, 4, 5, 6]
Function<T, Stream<R>>
函数式接口,可以理解为是一个转化Stream方法,T为输入,Stream<R>
为输出。
4 distinct()
去除重复元素,返回一个去重后的新Stream。
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 5, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 去除重复的数字 [1, 2, 3, 4, 5]
5 sorted()
对Stream中的元素进行排序,默认按照自然顺序进行排序。
List<Integer> numbers = Arrays.asList(5, 2, 4, 1, 3);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 排序后的数字列表 [1, 2, 3, 4, 5]
6 sorted(Comparator<T> comparator)
根据给定的Comparator对Stream中的元素进行排序。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> sortedNames = names.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// 按字母降序排序 ["Charlie", "Bob", "Alice"]
7 limit(long maxSize)
截取Stream中的前maxSize个元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// 截取前3个数字 [1, 2, 3]
8 skip(long n)
跳过Stream中的前n个元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> skippedNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
// 跳过前2个数字 [3, 4, 5]
9 forEach(Consumer<T> action)
对Stream中的每个元素执行给定的操作。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.forEach(System.out::println);
// 打印每个名字
10 collect(Collector<T, A, R> collector)
将Stream中的元素收集到一个集合或数据结构中,并返回结果。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> collectedNumbers = numbers.stream()
.collect(Collectors.toList());
// 收集到列表中 [1, 2, 3, 4, 5]
扩展(可以不看):
Collector<T, A, R> 是 Java 中用于将流中的元素进行收集的接口。它定义了三个泛型参数:
T:表示流中元素的类型。
A:表示用于累积部分结果的对象的类型,也可以看作是中间结果的类型。
R:表示收集操作的最终结果的类型。
Collector<T, A, R>
接口中定义了以下五个方法:
Supplier<A> supplier()
:用于创建一个初始的结果容器对象。
BiConsumer<A, T> accumulator()
:用于将流中的元素累积到结果容器中。
BinaryOperator<A> combiner()
:用于将两个结果容器合并为一个。
Function<A, R> finisher()
:用于将结果容器转换为最终结果。
Set<Characteristics> characteristics()
:用于获取收集器的特性,返回一个包含特性的不可变 Set 集合。
重点:
Collector<T, A, R> collector
JAVA8中有专门定义收集器的工具类Collectors,以下列出常用的方法:
toList()
将流中的元素收集到一个 List 中。
List<String> names = Stream.of("John", "Alice", "Bob")
.collect(Collectors.toList());
toSet()
将流中的元素收集到一个 Set 中。
Set<Integer> numbers = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.toSet());
toMap()
将流中的元素根据指定的键和值的提取函数收集到一个 Map 中。
Map<String, Integer> ageMap = Stream.of("John", "Alice", "Bob")
.collect(Collectors.toMap(Function.identity(), String::length));
counting()
对流中的元素进行计数。
long count = Stream.of("John", "Alice", "Bob")
.collect(Collectors.counting());
summingInt()
对流中的元素进行求和,返回一个整数。
int sum = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.summingInt(Integer::intValue));
averagingInt()
对流中的元素进行求平均值,返回一个 double 类型。
double average = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.averagingInt(Integer::intValue));
joining()
将流中的元素拼接成一个字符串。
String result = Stream.of("Hello", "World")
.collect(Collectors.joining(", "));
maxBy()
根据指定的比较器选择流中的最大元素。
Optional<String> maxName = Stream.of("John", "Alice", "Bob")
.collect(Collectors.maxBy(Comparator.naturalOrder()));
minBy()
根据指定的比较器选择流中的最小元素。
Optional<String> minName = Stream.of("John", "Alice", "Bob")
.collect(Collectors.minBy(Comparator.naturalOrder()));
groupingBy()
根据指定的分类函数对流中的元素进行分组。
Map<Character, List<String>> groupedNames = Stream.of("John", "Alice", "Bob")
.collect(Collectors.groupingBy(s -> s.charAt(0)));
partitioningBy()
根据指定的条件对流中的元素进行分区。
Map<Boolean, List<String>> partitionedNames = Stream.of("John", "Alice", "Bob")
.collect(Collectors.partitioningBy(s -> s.length() > 3));
mapping()
对流中的元素进行映射,并将结果收集到一个集合中。
List<Integer> lengths = Stream.of("John", "Alice", "Bob")
.collect(Collectors.mapping(String::length, Collectors.toList()));
filtering()
根据指定的条件过滤流中的元素,并将结果收集到一个集合中。
List<String> filteredNames = Stream.of("John", "Alice", "Bob")
.collect(Collectors.filtering(s -> s.length() > 3, Collectors.toList()));
reducing()
根据指定的规约操作对流中的元素进行规约。
Optional<Integer> sum = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.reducing(Integer::sum));
collectingAndThen()
对流中的元素进行收集,并执行一个最终的转换操作。
String longestName = Stream.of("John", "Alice", "Bob")
.collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(String::length)), Optional::get));
toCollection()
将流中的元素收集到指定的集合类型中。
LinkedList<String> names = Stream.of("John", "Alice", "Bob")
.collect(Collectors.toCollection(LinkedList::new));
summarizingInt()
对流中的元素进行统计,返回一个统计结果对象。
IntSummaryStatistics statistics = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.summarizingInt(Integer::intValue));
groupingByConcurrent()
并发地根据指定的分类函数对流中的元素进行分组。
ConcurrentMap<Character, List<String>> concurrentGroupedNames = Stream.of("John", "Alice", "Bob")
.collect(Collectors.groupingByConcurrent(s -> s.charAt(0)));
summingDouble()
对流中的元素进行求和,返回一个 double 类型。
double sum = Stream.of(1.0, 2.0, 3.0, 4.0, 5.0)
.collect(Collectors.summingDouble(Double::doubleValue));
toConcurrentMap()
将流中的元素根据指定的键和值的提取函数收集到一个并发的 Map 中。
ConcurrentMap<String, Integer> concurrentAgeMap = Stream.of("John", "Alice", "Bob")
.collect(Collectors.toConcurrentMap(Function.identity(), String::length));
11 toArray()
将Stream中的元素转换为数组。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer[] numberArray = numbers.stream()
.toArray(Integer[]::new);
// 转换为数组 [1, 2, 3, 4, 5]
12 anyMatch(Predicate<T> predicate)
判断Stream中是否存在满足给定条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean hasEvenNumber = numbers.stream()
.anyMatch(num -> num % 2 == 0);
// 是否有偶数,结果为true
13 allMatch(Predicate<T> predicate)
判断Stream中的所有元素是否都满足给定条件。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEvenNumbers = numbers.stream()
.allMatch(num -> num % 2 == 0);
// 是否都是偶数,结果为false
14 noneMatch(Predicate<T> predicate)
判断Stream中是否不存在满足给定条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noEvenNumber = numbers.stream()
.noneMatch(num -> num % 2 == 0);
// 是否没有偶数,结果为false
15 findFirst()
返回Stream中的第一个元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstNumber = numbers.stream()
.findFirst();
// 第一个数字,结果为Optional[1]
16 findAny()
返回Stream中的任意一个元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> anyNumber = numbers.stream().findAny();
// 任意一个数字,结果为Optional[1]或其他数字
17 count()
返回Stream中的元素个数。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream().count();
// 元素个数,结果为5