一. lambda表达式
1. lambda表达式基础语法格式
-
无参数,无返回值
(1)语法:() -> System.out.println("hello lambda");
(2)例子public void test1() { Runnable r1 = () -> System.out.println("hello lambda"); r1.run(); } // 函数式接口Runnable @FunctionalInterface public interface Runnable { public abstract void run(); }
-
有1个参数,无返回值
(1)语法:(x) -> System.out.println(x); 或 x -> System.out.println(x);
(2)说明:当只有一个参数时,可以省略参数的小括号。
(3)例子public void test2(){ // 定义函数式接口的实现 Consumer<String> con = (x) -> System.out.println(x); // 调用接口实现的方法 con.accept("有一个参数,无返回值"); } // 函数式接口Consumer @FunctionalInterface public interface Consumer<T> { void accept(T t); }
-
有2个参数,有返回值
(1)语法:(x, y) -> x + y
(2)例子
public void test3() {
BinaryOperator<Integer> binary = (x, y) -> x + y;
System.out.println(binary.apply(1, 2)); // 3
}
- 有多个参数
(1)说明:如果lambda表达式的代码块有多行语句,只需要用大括号{}把语句包含起来;对于有返回值和无返回值的情况,只有一个return的区别。如果只有一条语句,大括号和return都可以不用写。
(2)例子
public void test4() {
// 无返回值lambda函数体中用法
Runnable r1 = () -> {
System.out.println("hello lambda1");
System.out.println("hello lambda2");
System.out.println("hello lambda3");
};
r1.run();
// 有返回值lambda函数体中用法
BinaryOperator<Integer> binary = (x, y) -> {
int a = x * 2;
int b = y + 2;
return a + b;
};
System.out.println(binary.apply(1, 2));// 3
2. lambda表达式支持的方法引用和构造器引用
- 概述
- 如果lambda表达式的代码块只有一条代码,可以在代码块中使用方法引用和构造器引用。它们可以让Lambda表达式更简洁。
- 方法引用和构造器引用都需要使用两个英文冒号。
- 方法引用和构造器引用总览
种类 | 对应的lambda表达式 | 语法 | 说明 | 举例 |
---|---|---|---|---|
指向类方法的方法引用 | (a,b)->类名.类方法(a,b) | 类名::类方法 | 函数式接口被实现方法的全部参数传给该类方法作为参数 | (x)->Integer.parseInt(x) Integer::parseInt |
指向某类对象的实例方法的方法引用 | (a,b)->a.实例方法(b) | 某类对象的类名::实例方法 | 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 | (x,y)->x.equals(y) String::equals |
指向特有对象/现有对象的实例方法的方法引用 | (a,b,…)->特定对象.实例方法(a,b,…) | 特定对象::实例方法 | 函数式接口被实现方法的全部参数传给该方法作为参数 | x->System.out.println(x) System.out::println |
构造器的引用 | (a,b,…)->new 类名(a,b,…) | 类名::new | 函数式接口中被实现方法的全部参数传给该构造器作为参数 | ()->new Emp() Emp::new |
- 举例
@FunctionalInterface
interface Converter{
Integer convert(Stirng from);
}
public void test6() {
/*************** 方法引用 ****************/
// 1. 指向类方法的方法引用
Converter convert = from -> Integer.valueOf(from);
Converter convert = Integer::valueOf;
Integer val = convert.convert("99);
// 2. 指向某类对象的实例方法的方法引用
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp = String::equals;
System.out.println(bp.test("a", "b"));
// 3.1 指向特有对象的实例方法的方法引用
Converter convert2 = from -> "fkit".indexOf(from);
Converter convert2 = "fkit"::indexOf;
Integer val = convert2.convert("it);
// 3.2 指向现有对象的实例方法的方法引用
Consumer<String> con = x -> System.out.println(x);
Consumer<String> con = System.out::println;
con.accept("abc");
/*************** 构造器引用 ****************/
// 无参构造函数,创建实例
Supplier<Emp> supper = () -> new Emp();
Supplier<Emp> supper = Emp::new;
Emp emp = supper.get();
emp.setAddress("上海");
// 一个参数
Function<String, Emp> fun = address -> new Emp(address);
Function<String, Emp> fun = Emp::new;
System.out.println(fun.apply("beijing"));
// 两个参数
BiFunction<String, Integer, Emp> bFun = (name, age) -> new Emp(name, age);
BiFunction<String, Integer, Emp> bFun = Emp::new;
System.out.println(bFun.apply("xiaohong", 18));
}
class Emp {
private String address;
private String name;
private Integer age;
}
二. 函数式接口
1. 函数式接口简介
- 函数式接口定义:接口中只有一个抽象方法的接口,称为函数式接口。
- 说明
- 可以使用@FunctionalInterface注解修饰,对该接口做检查。如果接口里有多个抽象类,使用该注解会有语法错误。
- 在java8中,lambda表达式所用的接口,必须是函数式接口。
2. 常用的函数式接口介绍
2.1 JDK1.8之前已有的函数式接口
java.lang.Runnable
public abstract void run();
java.util.concurrent.Callable
V call();
java.util.Comparator
int compare(T o1, T o2);
java.security.PrivilegedAction
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
2.2 java8中java.util.function包下内置的核心接口
- Function<T,R> 函数型接口:接受一个输入参数,返回一个结果。
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
- Consumer 消费型接口:接受一个输入参数,无返回结果。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
- Supplier 供给型接口:无输入参数,返回一个结果。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- Predicate 断言型接口或判断型接口:接受一个输入参数,返回一个布尔型结果。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
2.3 java8中java.util.function包下内置的其他常用的函数式接口
- BiFunction<T,U,R> 函数式接口:接受两个输入参数的方法,并且返回一个结果。
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* Applies this function to the given arguments.
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
}
- BiConsumer<T,U> 函数式接口:接受两个输入参数的操作,并且不返回任何结果。
@FunctionalInterface
public interface BiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
}
- BiPredicate<T,U> 函数式接口:接受两个参数,返回一个布尔型结果。
@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u);
}
3. Java8 Function接口
- Funtion接口概述:定义了一个apply的抽象方法,接收一个泛型T对象,返回一个泛型R对象。
- 源码
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
- 举例
Function<Integer, Integer> function1 = x -> x * 2;
System.out.println(function1.apply(4));// 8
Function<Integer, String> function2 = x -> x * 2 + "dd";
System.out.println(function2.apply(4));//8dd
Function<String, Emp> objFunction1 = (str) -> new Emp(str);
System.out.println(objFunction1.apply("cc").getName());//cc
Function<String, Emp> objFunction2 = Emp::new;
System.out.println(objFunction2.apply("dd").getName());//dd
4. Java8 Consumer接口
- Consumer接口概述
- 接收一个泛型的参数T,然后调用accept,对这个参数做一系列的操作,没有返回值。
- 理解:Consumer是消费者的意思。它主要对入参做一些业务操作,没有返回值。在stream里,主要用于forEach方法。
- 源码
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
- 举例
Consumer<Integer> consumer = x -> {
int a = x + 2;
System.out.println(a);// 12
System.out.println(a + "_");// 12_
};
consumer.accept(10);
5. Java8 Supplier接口
- Supplier接口概述:这个接口是提供者的意思,提供一个对象,可以理解成创建对象的工厂。它传入一个泛型T参数,返回一个泛型T对象。
- 源码
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- 举例
// 这个接口,只是为我们提供一个创建好的对象
Supplier<String> supplier = String::new;
System.out.println(supplier.get()); //""
Supplier<Emp> supplierEmp = Emp::new;
Emp emp = supplierEmp.get();
emp.setName("dd");
System.out.println(emp.getName()); //dd
public static class Emp {
private String name;
}
6. Java8 BiConsumer接口
- 概述:BiConsumer接口接收两个泛型参数,对这两个参数做消费处理;使用这个函数式接口的终端操作常用的是遍历map
- 源码
@FunctionalInterface
public interface BiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
}
- 举例
Map<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");
map.put("c", "c");
map.put("d", "d");
map.forEach((k, v) -> {
System.out.println(k+“,”+v);
});
- 讲解:Map接口的终端操作,forEach的参数就是BiConsumer函数接口,对HashMap 的数据进行消费。
三. Java8 Stream接口
1. Stream简介
- 流(
Stream
)是java8 API中的新的成员,可以把它看成一个遍历数据的高级点的迭代器;也可以看做一个数据处理的工厂。此外,流还支持并行操作,也就不用去写复杂的多线程的代码。 Stream
的定义
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// 常用方法
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize);
Stream<T> skip(long n);
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Object[] toArray();
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
public static <T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
}
2. 流的创建
2.1 Arrays.stream()方法
- 概述:通过
Arrays
的静态方法stream()
传入一个泛型数组来创建流。public class Arrays { public static <T> Stream<T> stream(T[] array) { return stream(array, 0, array.length); } }
- 举例
String[] dd = { "a", "b", "c" };
Arrays.stream(dd).forEach(System.out::print);// abc
2.2 Stream.of() 方法
- 概述:通过
Stream
的静态方法of()
,传入一个泛型数组或者多个参数,来创建一个流。这个静态方法,也是调用了Arrays
的stream()
静态方法。public interface Stream<T> extends BaseStream<T, Stream<T>> { public static<T> Stream<T> of(T... values) { return Arrays.stream(values); } }
- 举例
String[] dd = { "a", "b", "c" };
Stream.of(dd).forEach(System.out::print);// abc
2.3 Collection的stream()方法
- 概述:这是
Collection
接口的默认方法,通过该方法创建一个流。继承Collection的接口
(如:Set
,List
,Map
,SortedSet
等)都可以使用该方法。public interface Collection<E> extends Iterable<E> { default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } }
- 举例
String[] dd = { "a", "b", "c" };
Arrays.asList(dd).stream().forEach(System.out::print);// abc
2.4 Stream.iterate()方法
-
概述
- 它是
Stream
接口下的一个静态方法。它以迭代器的形式,创建一个数据流。 iterate()
方法传入两个参数:一个是泛型T对象,表示数据的起始;一个是函数式接口UnaryOperator
。从hasNext()
方法中可以看到,返回值一直为true,它表示迭代器会一直执行下去,创建的数据集合值为泛型T对象。这样一直创建无限个对象的流,也称为无限流。
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) { Objects.requireNonNull(f); final Iterator<T> iterator = new Iterator<T>() { @SuppressWarnings("unchecked") T t = (T) Streams.NONE; @Override public boolean hasNext() { return true; } @Override public T next() { return t = (t == Streams.NONE) ? seed : f.apply(t); } }; return StreamSupport.stream(Spliterators.spliteratorUnknownSize( iterator, Spliterator.ORDERED | Spliterator.IMMUTABLE), false); }
- 它是
-
举例
Stream.iterate(0, x -> x + 1).limit(10).forEach(System.out::print);// 0123456789
2.5 Stream.generate()方法
-
概述
- 它是
Stream
中的一个静态方法,也是无限生成对象的集合流,也是一个无限流。 generate()
方法传入一个函数式接口Supplier
。
public static<T> Stream<T> generate(Supplier<T> s) { Objects.requireNonNull(s); return StreamSupport.stream(new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false); }
- 它是
-
举例
Stream.generate(() -> "x").limit(10).forEach(System.out::print);// xxxxxxxxxx
2.6 总结
2.1~2.3
:根据具体的数组或集合对象创建的流。在创建流之前,这些对象的大小(长度)已经确认,所以通过这种方式创建的流,也被称为有限流。2.4~2.5
:创建的是无限大小的流(generate()
方法最大是Long.MAX_VALUE
),也被称为无限流。然而,我们不可能就这样放任对象被无限创建,直到内存溢出,这样的无限流,也是配合limit()
方法使用,指定这个流生成元素的个数。
3. Stream接口的中间操作和终端操作
3.1 概述
Stream
接口的方法可以分成两种类型:- 中间操作:返回类型为接口本身的
Stream<T>
。 - 终端操作:返回类型为其他对象类型。
- 中间操作:返回类型为接口本身的
- 常用中间操作和终端操作的概览图
方法 | 方法类型 | 返回类型 | 参数 | 函数描述符 |
---|---|---|---|---|
filter | 中间操作 | Stream<T> | Predicate<T> | T -> boolean |
map | 中间操作 | Stream<R> | Function<T,R> | T -> Stream<R> |
flatMap | 中间操作 | Stream<R> | Function<T,Stream<R>> | T -> Stream<R> |
distinct | 中间操作 | Stream<T> | ||
sorted | 中间操作 | Stream<T> | ||
sorted | 中间操作 | Stream<T> | Comparator<T> | <T,T> -> int |
limit | 中间操作 | Stream<T> | long | |
skip | 中间操作 | Stream<T> | long | |
forEach | 终端操作 | void | Consumer<T> | T -> void |
collect | 终端操作 | R | Collector<T,A,R> | |
count | 终端操作 | long | ||
anyMatch | 终端操作 | boolean | Predicate<T> | T -> boolean |
allMatch | 终端操作 | boolean | Predicate<T> | T -> boolean |
noneMatch | 终端操作 | boolean | Predicate<T> | T -> boolean |
3.2 filter操作
- 方法定义
Stream<T> filter(Predicate<? super T> predicate);
- 方法说明
filter()
方法传入一个Predicate
的函数接口,这个接口传入一个泛型参数T,做完操作之后,返回一个boolean
值;filter()
方法的作用,是对这个boolean
做判断,返回true判断之后的对象。
- 举例
String[] dd = { "a", "b", "c" };
Arrays.stream(dd).filter(str -> str.equals("a")).forEach(System.out::println);//返回字符串为a的值
3.3 map操作
- 方法定义
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
- 方法概述
map()
方法传入一个Function的函数式接口,这个接口接收一个泛型T,返回泛型R,即可以改变返回的类型。
- 举例
// 把Integer变成了String输出
Integer[] dd = { 1, 2, 3 };
Arrays.stream(dd).map(str -> Integer.toString(str)).forEach(str -> {
System.out.println(str);// 1 ,2 ,3
System.out.println(str.getClass());// class java.lang.String
});
// 把Emp对象里的name字符串单独输出
List<Emp> list = Arrays.asList(new Emp("a"), new Emp("b"), new Emp("c"));
list.stream().map(emp -> emp.getName()).forEach(str -> {
System.out.println(str);
});
3.4 flatMap操作
- 方法定义
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
- 方法概述
- 与
map()
一样,flatMap()
方法也接收一个Function
的函数式接口,并且方法返回都是Stream<R>
。 - 与
map()
不同,flatMap()
方法传入的Function
函数式接口接收一个范型T,返回Stream<R>
。flatMap()
方法的作用是把两个流,变成一个流返回。
- 与
- 举例(
map
方法和flatMap
方法的对比)
// map()方法
String[] words = new String[]{"Hello","World"};
List<String[]> a = Arrays.stream(words).map(word -> word.split("")).distinct().collect(toList());
a.forEach(System.out::print); // 返回一个包含两个String[]的list,即[Ljava.lang.String;@12edcd21 和 [Ljava.lang.String;@34c45dca
// flatMap()方法
String[] words = new String[]{"Hello","World"};
List<String> a = Arrays.stream(words).map(word -> word.split("")).flatMap(str -> Arrays.stream(str)).distinct().collect(toList());
a.forEach(System.out::print); // HeloWrd
3.5 distinct,sorted,peek,limit,skip操作
- 方法定义
//去重
Stream<T> distinct();
//排序
Stream<T> sorted();
//根据Comparator指定属性排序
Stream<T> sorted(Comparator<? super T> comparator);
//对对象的进行操作
Stream<T> peek(Consumer<? super T> action);
//截断--取前maxSize个对象
Stream<T> limit(long maxSize);
//截断--忽略前n个对象
Stream<T> skip(long n);
- 举例
// 对数组流,先过滤重复,在排序,再控制台输出 1,2,3
Arrays.asList(3, 1, 2, 1).stream().distinct().sorted().forEach(str -> {
System.out.println(str);
});
// 对list里的emp对象,取出薪水,并对薪水进行排序,然后输出薪水的内容,map操作,改变了Strenm的泛型对象
List<Emp> list = getEmpInfo();
list.stream().map(emp -> emp.getSalary()).sorted().forEach(salary -> {
System.out.println(salary);
});
// 根据emp的属性name,进行排序
println(list.stream().sorted(Comparator.comparing(Emp::getName)));
// 给年龄大于30岁的人,薪水提升1.5倍,并输出结果
Stream<Emp> stream = list.stream().filter(emp -> {
return emp.getAge() > 30;
}).peek(emp -> {
emp.setSalary(emp.getSalary() * 1.5);
});
stream.forEach(emp -> {
System.out.println(String.format("名字:%s,年龄:%s,薪水:%s", emp.getName(), emp.getAge(), emp.getSalary()));
});
// 数字从1开始迭代(无限流),下一个数字是上个数字+1。忽略前5个并且只取10个数字。
// 原本1~无限大,跳过前5个(数字1~5),即从6开始,截取10个,因此最终结果是6~15
Stream.iterate(1, x -> ++x).skip(5).limit(10).forEach(System.out::println);
3.6 toArray操作
- 方法概述:用于Stream流转数组,也可用于优雅地将List转为数组。
- 方法定义
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
@FunctionalInterface
public interface IntFunction<R> {
R apply(int value);
}
- 说明
- 如果
新建数组的size小于等于原list
或stream的size,则所有元素都转化为新建数组中元素,且大小为数组大小。 - 如果新建数组的size>list或stream的size,则补充默认值。
- 如果
- 举例
/** 原先 List->数组 的方式 */
private static void listToStringArray(List lists) {
List<String> list = Arrays.asList("a", "b", "c");
String[] strs = (String[]) list.toArray(new String[3]); // a b c
}
/** Stream流的toArray()方法 */
// 1. Stream流转换为int[]
private static void intStreamToIntArray() {
int[] arr = IntStream.of(1, 2, 3, 4, 5).toArray();
Integer[] arr = Stream.of(1, 2, 3, 4, 5).toArray(Integer[]::new);
System.out.println(Arrays.toString(arr));
}
// 2. Stream流转换为String[]
private static void StreamToStringArray(List list) {
// 法1:使用lambda表达式(List->数组)
String[] arr1 = (String[]) list.stream().toArray(size -> {
return new String[size];
});
//法2:使用方法引用(List->数组)--推荐
String[] arr2 = (String[]) list.stream().toArray(String[]::new);
printArray(arr2);
}
3.7 collect操作
- 方法定义
<R, A> R collect(Collector<? super T, A, R> collector);
- 说明:
collector()
方法的参数一般传入Collectors类的静态方法,因为Collectors静态工厂类的方法几乎可以满足日常的所有操作。
- Collectors静态工厂类概述
Collectors 类的静态方法 | collect() 方法的返回类型 | 用于 |
---|---|---|
Collector<T, ?, List<T>> toList() | List<T> | 把流中所有元素收集到一个List |
Collector<T, ?, Set<T>> toSet() | Set<T> | 把流中所有元素收集到一个Set ,删除重复项 |
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper) | Map<K,U>> | 把流中所有元素收集到一个Map ,指定key和value |
<T> Collector<T, ?, Long> counting() | Long | 计算流中的元素个数 |
<T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper) | Integer | 对流中的一个整数属性求和 |
<T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper) | Double | 流中所有元素的平均值 |
Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) | IntSummaryStatistics | 流中所有元素的综合处理 |
<T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator) | Optional<T>> | 最大值 |
<T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator) | Optional<T>> | 最小值 |
Collector<CharSequence, ?, String> joining() | String | 连接字符串 |
Collector<CharSequence, ?, String> joining(CharSequence delimiter) | String | 按指定的分隔符连接字符串 |
Collector<CharSequence, ?, String> joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix) | String | 按指定的分隔符,前缀和后缀连接字符串 |
Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) | Map<K, List<T>> | 分组操作 |
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) | Map<Boolean, List<T>> | 分区操作 |
- 举例
// 转list
List<String> names = list.stream().map(emp -> emp.getName()).collect(Collectors.toList());
// 转set
Set<String> address = list.stream().map(emp -> emp.getName()).collect(Collectors.toSet());
// 转map 需要指定key和value,Function.identity()表示当前的Emp对象本身
Map<String, Emp> map = list.stream().collect(Collectors.toMap(Emp::getName, Function.identity()));
// 计算元素中的个数
Long count = list.stream().collect(Collectors.counting());
// 数据求和 [summingInt summingLong,summingDouble]
Integer sumAges = list.stream().collect(Collectors.summingInt(Emp::getAge));
// 平均值 [(]averagingInt,averagingDouble,averagingLong]
Double aveAges = list.stream().collect(Collectors.averagingInt(Emp::getAge));
// 综合处理(求最大值,最小值,平均值,求和操作)
// [summarizingInt,summarizingLong,summarizingDouble]
IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(Emp::getAge));
System.out.println(intSummary.getAverage());// 19.5
System.out.println(intSummary.getMax());// 22
System.out.println(intSummary.getMin());// 17
System.out.println(intSummary.getSum());// 117
// 连接字符串 也可以使用重载方法,加一些前缀、后缀和中间分隔符
String strEmp = list.stream().map(emp -> emp.getName()).collect(Collectors.joining());
String strEmp1 = list.stream().map(emp -> emp.getName()).collect(Collectors.joining("-中间的分隔符-"));
String strEmp2 = list.stream().map(emp -> emp.getName()).collect(Collectors.joining("-中间的分隔符-", "前缀*", "&后缀"));
System.out.println(strEmp);// 小名小红小蓝小灰小黄小白
System.out.println(strEmp1); // 小名-中间的分隔符-小红-中间的分隔符-小蓝-中间的分隔符-小灰-中间的分隔符-小黄-中间的分隔符-小白
System.out.println(strEmp2); // 前缀*小名-中间的分隔符-小红-中间的分隔符-小蓝-中间的分隔符-小灰-中间的分隔符-小黄-中间的分隔符-小白&后缀
// 最大值 按照比较器中的比较结果刷选
Optional<Integer> maxAge = list.stream().map(emp -> emp.getAge()).collect(Collectors.maxBy(Comparator.comparing(Function.identity())));
// 最小值
Optional<Integer> minAge = list.stream().map(emp -> emp.getAge()).collect(Collectors.minBy(Comparator.comparing(Function.identity())));
// 归约操作
list.stream().map(emp -> emp.getAge()).collect(Collectors.reducing((x, y) -> x + y));
list.stream().map(emp -> emp.getAge()).collect(Collectors.reducing(0, (x, y) -> x + y));
// 分组操作
Map<String, List<Emp>> mapGroup = list.stream().collect(Collectors.groupingBy(Emp::getAddress)); // 根据地址,把原list进行分组
// 分区操作 需要根据条件指定判断分区
Map<Boolean, List<Integer>> partitioningMap = list.stream().map(emp -> emp.getAge()).collect(Collectors.partitioningBy(emp -> emp > 20));
3.8 count, anyMatch, allMatch, noneMatch操作
- 概述
- count方法:返回集合流的元素长度
- anyMatch方法:表示任意一个元素为判断条件为真,则方法返回true。
- allMatch方法:表示所有元素的判断条件为真,则返回true。
- noneMatch方法:表示所有元素的判断条件都不为真,则返回true。
- 例子
List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
boolean bb = strs.stream().allMatch(str -> str.equals("a"));
boolean cc = strs.stream().noneMatch(str -> str.equals("a"));
long count = strs.stream().filter(str -> str.equals("a")).count();
System.out.println(aa); // TRUE
System.out.println(bb); // FALSE
System.out.println(cc); // FALSE
System.out.println(count); // 4
3.9 forEach和forEachOrdered操作
- 方法定义
// 串行流按顺序输出,并行流遍历时对顺序无保证
void forEach(Consumer<? super T> action);
// 串行流按顺序输出,并行流遍历时严格按照顺序取数据
void forEachOrdered(Consumer<? super T> action);
- 举例
List<String> strs = Arrays.asList("a", "b", "c");
/**
* 1. 使用stream流
*/
strs.stream().forEach(System.out::print);//abc
strs.stream().forEachOrdered(System.out::print);//abc
/**
* 2. 使用parallelStream流
*/
strs.parallelStream().forEach(System.out::print);//bca
strs.parallelStream().forEachOrdered(System.out::print);//abc
- 说明
- stream流:是一个串行流,即串行执行,所有遍历的结果都是按照集合元素放入的顺序;
- parallelStream流:是一个并行流,即在程序内部迭代的时候,会并行处理。
- 在并行程序中,forEach在并行输出时可能会随机排列。但是,如果对处理之后的数据没有顺序的要求,使用forEach的效率较高。