函数式接口
函数式接口简介
定义:有且仅有一个抽象方法的接口
@FunctionalInterface注解:可以检测接口是否是函数式接口,不是函数式接口无法通过编译
@FunctionalInterface
public interface HeferyFunctionalInterface {
// 定义抽象方法
public abstract void method();
}
使用:一般可以作为方法的参数和返回值类型
public class FunctionalInterfaceDemo {
// 声明方法,参数使用函数式接口 HeferyFunctionalInterface
public static void show(HeferyFunctionalInterface heferyFunctionalInterface) {
heferyFunctionalInterface.method();
}
public static void main(String[] args) {
// 调用 show() 方法,方法参数是接口,可以传递接口的匿名内部类
show(new HeferyFunctionalInterface() {
@Override
public void method() {
System.out.println("使用 匿名内部类 重写接口中的抽象方法");
}
});
// 调用 show() 方法,方法参数是函数式接口,可以使用Lambda表达式
show( ()->{
System.out.println("使用 Lambda表达式 重写接口中的抽象方法");
});
}
}
方法引用
指向静态方法的方法引用
/**
* (args) -> ClassName.staticMethod(args)
* ClassName::staticMethod
*/
public void test1() {
Consumer<String> consumer1 = (String number) -> Integer.parseInt(number);
Consumer<String> consumer2 = Integer::parseInt;
}
指向任意类型实例方法的方法引用
/**
* (args) -> args.instanceMethod()
* ClassName::instanceMethod
*/
public void test2() {
Consumer<String> consumer1 = (String str) -> str.length();
Consumer<String> consumer2 = String::length
}
指向现有对象的实例方法的方法引用
/**
* (args) -> object.instanceMethod(args)
* object::instanceMethod
*/
public void test3() {
StringBuilder stringBuilder = new StringBuilder();
Consumer<String> consumer1 = (String str) -> stringBuilder.append(str);
Consumer<String> consumer2 = stringBuilder::append
}
常用函数接口
Supplier
生产型接口,获取一个泛型参数指定类型的一对象数据
@FunctionalInterface
public interface Supplier<T> {
T get();
}
public class SupplierDemo {
// 需求:求出数组最大值:定义一个方法,方法的参数传递 SuppLier<T)接口
public static int getListMax(Supplier<Integer> sup) {
return sup.get();
}
public static void main(String[] args) {
int[] array = {1, 34, 56, 78, 3, 545, 43};
// 调用 getListMax() 方法,方法的参数 SuppLier 是一个函数式接口,所以可以传递Lambda表达式
int max = getListMax( () -> {
int maxValue = array[0];
for (int i : array) {
if (maxValue < i) {
maxValue = i;
}
}
return maxValue;
});
System.out.println(max); // 545
}
}
Consumer
消费型接口,泛型执行什么类型,就可以使用 accept 方法消费什么类型的数据(用于接收一个对象进行处理但没有返回)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
public class ConsumerDemo {
// 需求:求出数组最大值:定义一个方法,方法的参数传递 SuppLier<T)接口
public static void consumerMethod(String name, Consumer<String> con) {
con.accept(name);
}
// 需求:需要两个consumer接口,可以把两个Consumer接口组合到一起,在对数据进行消费
public static void consumerAndThen(String name, Consumer<String> con1, Consumer<String> con2) {
/*con1.accept(name);
con2.accept(name);*/
con1.andThen(con2).accept(name); // 谁写前边谁先消费
}
public static void main(String[] args) {
consumerMethod("hefery", (name) -> {
// 消费字符串:反转
String reName = new StringBuilder(name).reverse().toString();
System.out.println(reName); // yrefeh
});
consumerAndThen("hefery",
(name) -> {
// 大写输出
System.out.println(name.toUpperCase()); // HEFERY
},
(name) -> {
// 小写输出
System.out.println(name.toLowerCase()); // hefery
}
);
}
}
Predicate
对某种类型的数据进行判断,从而得到一个boolean值结果
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
/** && */
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/** ! */
default Predicate<T> negate() {
return (t) -> !test(t);
}
/** || */
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
public class PredicateDemo {
// 判断字符串:长度是否大于5,满足返回true
public static boolean checkString(String str, Predicate<String> predicate) {
return predicate.test(str);
}
// 判断字符串:长度是否大于5 and 包含H
public static boolean checkStringAnd(String str, Predicate<String> predicate1, Predicate<String> predicate2) {
//return predicate1.test(str) && predicate2.test(str);
return predicate1.and(predicate2).test(str);
}
// 判断字符串:长度是否大于5 or 包含H
public static boolean checkStringOr(String str, Predicate<String> predicate1, Predicate<String> predicate2) {
//return predicate1.test(str) || predicate2.test(str);
return predicate1.or(predicate2).test(str);
}
// 判断字符串:长度是否大于5,满足返回false
public static boolean checkStringNegate(String str, Predicate<String> predicate) {
//return !predicate.test(str);
return predicate.negate().test(str);
}
public static void main(String[] args) {
String hefery = "hefery";
// 调用 checkString 方法对字符串进行校验,参数传递字符串和Lambda表达式
boolean b1 = checkString(hefery, (String str) -> {
return str.length() > 5;
});
boolean b2 = checkString(hefery, str -> str.length() > 5);
System.out.println(b1); // true
System.out.println(b2); // true
// 调用 checkStringAnd 方法对字符串进行校验,参数传递字符串和Lambda表达式
boolean b3 = checkStringAnd(hefery,
(String str) -> {return str.length() > 5;},
(String str) -> {return str.contains("H");}
);
System.out.println(b3); // false
// 调用 checkStringOr 方法对字符串进行校验,参数传递字符串和Lambda表达式
boolean b4 = checkStringOr(hefery,
(String str) -> {return str.length() > 5;},
(String str) -> {return str.contains("H");}
);
System.out.println(b4); // true
// 调用 checkStringNegate 方法对字符串进行校验,参数传递字符串和Lambda表达式
boolean b5 = checkStringNegate(hefery, (String str) -> {
return str.length() > 5;
});
System.out.println(b5); // false
}
}
Function
转换一个对象为不同类型的对象
@FunctionalInterface
public interface Function<T, R> {
/** */
R apply(T t);
/** */
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/** */
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/** */
static <T> Function<T, T> identity() {
return t -> t;
}
}
public class FunctionDemo {
// 把字符串类型的整数转换为 Integer 类型的整数
public static void change(String str, Function<String, Integer> function) {
Integer apply = function.apply(str);
if (apply instanceof Integer) {
System.out.println(apply); // 1234
}
}
// 需求:把string类型的“1234",转换为Inteter类型,把转换后的结果加10,把增加之后的Integer类型的数据,转换为String类型
public static void changeAndThen(String str, Function<String, Integer> function1, Function<Integer, String> function2) {
String ss = function1.andThen(function2).apply(str);
if (ss instanceof String) {
System.out.println(ss); // 1244
}
}
public static void main(String[] args) {
String str = "1234";
// 调用 change 方法,传递字符串类型的整数和Lambdo表达式
change(str, (String string) -> {
return Integer.parseInt(string);
});
change(str, string -> Integer.parseInt(string) );
// 调用 changeandThen 方法,传递字符串类型的整数和Lambdo表达式
changeAndThen(str,
//(String string) -> { return Integer.parseInt(string) + 10; },
string -> Integer.parseInt(string) + 10,
//(Integer integer) -> { return integer + ""; }
integer -> integer + ""
);
}
}
Lambda
Lambda简介
JDK1.8 引入函数式编程风格,通过行为参数化传递代码。
Lambda使用
Lambda语法
形式:
- (parameters) -> expression
- (parameters) -> {statement;}+ 没有参数:() -> System. out. println("Hello World!");
参数:
- 单个参数:name -> System. out. println("Hello World from"+name+"!");
- 没有参数,逻辑复杂:() ->{ System.out.println("Hello"); System.out.println("World"); }
- 两个参数
BinaryOperatorfunctionAdd = (x,y) -> x+y;
Long result = functionAdd. apply(1L,2L); - 参数显示声明
BinaryOperatorfunctionAdd = (Long x, Long y) -> x+y;
Long result = functionAdd. apply(1L, 2L);
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
使用Lambda作为参数
Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数
java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就可以使用 Lambda 进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别
public class RunnableDemo {
// 定义 startThread() 方法参数使用函数式接口
public static void startThread(Runnable runnable) {
// 开启多线程
new Thread(runnable).start();
}
public static void main(String[] args) {
// 调用 startThread() 方法,方法的参数是一个接口,那么可以传递这个接口的匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " --> " + "线程已启动"); // Thread-0 --> 线程已启动
}
});
// 调用 startThread() 方法,方法的参数是一个函数式接口,所以可以传递Lambda表达式
startThread( () -> {
System.out.println(Thread.currentThread().getName() + " --> " + "线程已启动"); // Thread-1 --> 线程已启动
});
}
}
使用Lambda作为返回值
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个 Lambda 表达式。当需要通过一个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取
public class RunnableDemo {
// 定义方法的返回值类型使用函数式接口 Comparator
public static Comparator<String> getComparator() {
// 1. 方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 字符串降序
return o2.length() - o1.length();
}
};
// 2. 方法的返回值类型是一个函数式接口,那么我们可以返回这个接口的Lambda表达式
return (String o1, String o2) -> {
// 字符串降序
return o2.length() - o1.length();
};
// 3. 简化 Lambda
return (o1, o2) -> o2.length() - o1.length();
}
public static void main(String[] args) {
String[] array = {"aaa", "bbb", "cccc", "DDDDD"};
// 未排序
Arrays.stream(array).forEach(System.out::println);
System.out.println("===========================");
// 排序
Arrays.sort(array, getComparator());
Arrays.stream(array).forEach(System.out::println);
}
}
Stream
Stream简介
Stream:流,是一个来自数据源的元素队列并支持聚合操作
相关概念:
- 元素:特定类型的对象,形成一个队列 Java 中的 Stream 并不会存储元素,而是按需计算
- 数据源:流的来源。 可以是集合,数组,I/O channel, 构造器 generator 等
- 聚合操作:类似SQL语句一样的操作, 比如 filter, map, reduce, find, match, sorted 等
组成:数据源 --> 中间操作(filter、sorted、map)--> 终端操作(collect)
作用:
- 极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码
- 将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等
特点:
Stream应用
Stream创建
- 值创建流
- 数组创建流
- 文件生成流
- 函数生成流(无限流)
- stream():为集合创建串行流
- parallelStream():为集合创建并行流
所有的 Collection 集合都可以通过 stream 默认方法获取流
Stream 接口的静态方法 of 可以获取数组对应的流
public class StreamDemo {
public static void main(String[] args) {
// 集合转换为Stream
ArrayList<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
HashSet<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
HashMap<String, String> map = new HashMap<>();
// key -> value
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> entryStream = entries.stream();
// key存储在Map
Set<String> keySet = map.keySet();
Stream<String> mapStream = keySet.stream();
// value存储在Collection
Collection<String> valueCollection = map.values();
Stream<String> collectionStream = valueCollection.stream();
// 数组转换为Stream
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
String[] array = {"hefery", "lalala", "hahaha"};
Stream<String> integerListStream = Stream.of(array);
}
}
Stream操作
- 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用
- 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。终结方法包括 count 和 forEach 方法
public class StreamDemo {
public static void main(String[] args) {
// POJO
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 23, "male", "Washington"));
personList.add(new Person("Lily", 7800, 24, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 55, "male", "New York"));
personList.add(new Person("Alisa", 7900, 67, "female", "New York"));
// String数组
List<String> stringList = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
// Integer数组
List<Integer> integerList = Arrays.asList(7, 6, 9, 4, 11, 6);
}
}
遍历
- foreach:forEach(System.out::println)
- peek
过滤
- filter:通过设置的条件过滤出元素
匹配
- findFirst:匹配第一个
- findAny:匹配任意
- anyMatch:filter + findAny,是否存在
- allMatch:全部匹配
- noneMatch:不匹配
public class StreamDemo {
public static void main(String[] args) {
// 筛选 age>24 的 Person,并取第一个
Optional<Person> first = personList.stream().filter(person -> person.getAge() > 24).findFirst();
Optional<Person> any = personList.stream().filter(person -> person.getAge() > 24).findAny();
boolean anyMatch = personList.stream().anyMatch(person -> person.getAge() > 24);
System.out.println("age 大于 24 的第一人:" + first); // age 大于 24 的第一人:Optional[Person(name=Owen, salary=9500, age=55, sex=male, area=New York)]
System.out.println("age 大于 24 的任一人:" + any); // age 大于 24 的任一人:Optional[Person(name=Owen, salary=9500, age=55, sex=male, area=New York)]
System.out.println("是否存在 age 大于 24 的人:" + anyMatch); // 是否存在 age 大于 24 的人:true
}
}
聚合
- distinct:去重
- limit:限制
- skip:跳过
public class StreamDemo {
public static void main(String[] args) {
// 获取String集合中最长的元素
Optional<String> maxString = stringList.stream().max(Comparator.comparing(String::length));
System.out.println("最长的 String:"+ maxString); // 最长的 String:Optional[weoujgsd]
// 自然排序
Optional<Integer> maxInteger1 = integerList.stream().max(Integer::compareTo);
// 自定义排序
Optional<Integer> maxInteger2 = integerList.stream().max(
(o1, o2) -> o1.compareTo(o2)
);
/*Optional<Integer> maxInteger2 = list2.stream().max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});*/
System.out.println("自然排序的最大值:" + maxInteger1.get()); // 自然排序的最大值:11
System.out.println("自定义排序的最大值:" + maxInteger2.get()); // 自定义排序的最大值:11
// 获取员工工资最高的人
Optional<Person> maxSalaryPerson = personList.stream().max(Comparator.comparingInt(Person::getSalary));
System.out.println("员工工资最大值:" + maxSalaryPerson.get().getSalary()); // 员工工资最大值:9500
// 计算Integer集合中大于6的元素的个数
long count = integerList.stream().filter(x -> x>6).count();
System.out.println("integerList中大于6的元素个数:" + count); // integerList中大于6的元素个数:3
// concat:合并两个流 distinct:去重
String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
List<String> newConcat = Stream.concat(Stream.of(arr1), Stream.of(arr2)).distinct().collect(Collectors.toList());
// limit:限制从流中获得前n个数据
List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
// skip:跳过前n个数据
List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("流合并:" + newConcat); // 流合并:[a, b, c, d, e, f, g]
System.out.println("limit:" + collect); // limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
System.out.println("skip:" + collect2); // skip:[3, 5, 7, 9, 11]
}
}
映射
- map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
public class StreamDemo {
public static void main(String[] args) {
// 字符串数组的元素全部改为大写。整数数组每个元素+3
stringList.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println); // ADNM ADMMT POT XBANGD WEOUJGSD
integerList.stream().map(x -> x+3).collect(Collectors.toList()).forEach(System.out::println); // 10 9 12 7 14 9
// 将员工的薪资全部增加1000
//personList.stream().map(person -> person.getSalary()+1000).collect(Collectors.toList()).forEach(System.out::println);
// 不改变原来员工集合的方式
List<Person> personListNew = personList.stream().map(person -> {
Person personNew = new Person(person.getName(), 0, 0, null, null);
personNew.setSalary(person.getSalary() + 1000);
return personNew;
}).collect(Collectors.toList());
System.out.println("一次改动前:" + personList.get(0).getName() + "-->" + personList.get(0).getSalary()); // 一次改动前:Tom-->8900
System.out.println("一次改动后:" + personListNew.get(0).getName() + "-->" + personListNew.get(0).getSalary()); // 一次改动后:Tom-->9900
// 改变原来员工集合的方式
List<Person> personListNew2 = personList.stream().map(person -> {
person.setSalary(person.getSalary() + 1000);
return person;
}).collect(Collectors.toList());
System.out.println("二次改动前:" + personList.get(0).getName() + "-->" + personListNew.get(0).getSalary()); // 二次改动前:Tom-->9900
System.out.println("二次改动后:" + personListNew2.get(0).getName() + "-->" + personListNew.get(0).getSalary()); // 二次改动后:Tom-->9900
// 将两个字符数组合并成一个新的字符数组
List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
List<String> listNew = list.stream().flatMap(s -> {
// 将每个元素转换成一个stream
String[] split = s.split(",");
Stream<String> s2 = Arrays.stream(split);
return s2;
}).collect(Collectors.toList());
System.out.println("处理前的集合:" + list); // 处理前的集合:[m,k,l,a, 1,3,5,7]
System.out.println("处理后的集合:" + listNew); // 处理后的集合:[m, k, l, a, 1, 3, 5, 7]
// Integer集合的元素之和、乘积和最大值
// 求和
Optional<Integer> sum1 = integerList.stream().reduce((x, y) -> x + y);
Optional<Integer> sum2 = integerList.stream().reduce(Integer::sum);
Integer sum3 = integerList.stream().reduce(0, Integer::sum);
// 乘积
Optional<Integer> product = integerList.stream().reduce((x, y) -> x * y);
// 最大值
Optional<Integer> max1 = integerList.stream().reduce((x, y) -> x > y ? x : y);
Integer max2 = integerList.stream().reduce(1, Integer::max);
System.out.println("integerList求和:" + sum1.get() + "," + sum2.get() + "," + sum3); // integerList求和:43,43,43
System.out.println("integerList求积:" + product.get()); // integerList求积:99792
System.out.println("integerList求最值:" + max1.get() + "," + max2); // integerList求最值:11,11
// 所有员工的工资之和和最高工资
// 工资之和
Optional<Integer> sumSalary1 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
Integer sumSalary2 = personList.stream().reduce(
0,
(sum, p) -> sum += p.getSalary(),
(sumSalaryTemp1, sumSalaryTemp2) -> sumSalaryTemp1 + sumSalaryTemp2
);
Integer sumSalary3 = personList.stream().reduce(
0,
(sum, p) -> sum += p.getSalary(),
Integer::sum
);
// 最高工资
Integer maxSalary1 = personList.stream().reduce(
0,
(max, p) -> max > p.getSalary() ? max : p.getSalary(),
Integer::max
);
Integer maxSalary2 = personList.stream().reduce(
0,
(max, p) -> max > p.getSalary() ? max : p.getSalary(),
(maxSalaryTemp1,maxSalaryTemp2) -> maxSalaryTemp1 > maxSalaryTemp2 ? maxSalaryTemp1 : maxSalaryTemp2
);
System.out.println("工资之和:" + sumSalary1.get() + "," + sumSalary2 + "," + sumSalary3); // 工资之和:55300,55300,55300
System.out.println("最高工资:" + maxSalary1 + "," + maxSalary2); // 最高工资:10500,10500
}
}
归约
- reduce:把 Stream 流缩减成一个值,能实现对集合求和、求乘积和求最值操作
- max:最大
- min:最小
- count:计数
收集
-
concat:合并两个流
-
collect:将处理完的流存入新集合,依赖 Collectors 类内置的静态方法
- 计数:count
- 平均值:averagingInt、averagingLong、averagingDouble
- 最值:maxBy、minBy
- 求和:summingInt、summingLong、summingDouble
- 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
- 接合:joining,将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串
收集器接口 Collectors
- toList:
- toSet:
- toMap:
- toCollection:
- toConcurrentMap:
Collectors.partitioningBy(item -> item%2==0) // 按奇偶分区
Collectors.groupingBy(item -> item) // 按元素分区
public class StreamDemo {
public static void main(String[] args) {
// 筛选员工中工资高于8000的人,并形成新的集合
List<Person> fiterList = personList.stream().filter(x -> x.getSalary()>8000).collect(Collectors.toList());
System.out.println("高于8000的员工姓名:" + fiterList); // 高于8000的员工姓名:[Person(name=Tom, salary=8900, age=23, sex=male, area=New York), Person(name=Anni, salary=8200, age=24, sex=female, area=New York), Person(name=Owen, salary=9500, age=55, sex=male, area=New York)]
// toList、toSet、toMap
List<Integer> arrayList = integerList.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
Set<Integer> set = integerList.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toMap(Person::getName, p -> p));
System.out.println("toList:" + arrayList); // toList:[6, 4, 6]
System.out.println("toSet:" + set); // toSet:[4, 6]
System.out.println("toMap:" + map); // toMap:{Tom=Person(name=Tom, salary=9900, age=23, sex=male, area=New York), Owen=Person(name=Owen, salary=10500, age=55, sex=male, area=New York), Anni=Person(name=Anni, salary=9200, age=24, sex=female, area=New York), Alisa=Person(name=Alisa, salary=8900, age=67, sex=female, area=New York), Lily=Person(name=Lily, salary=8800, age=24, sex=female, area=Washington)}
// 求总数
Long countPerson = personList.stream().collect(Collectors.counting());
// 求平均工资
Double averagePersonSalary = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
// 求最高工资
Optional<Integer> maxPersonSalary = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
// 求工资之和
Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
// 一次性统计所有信息
DoubleSummaryStatistics collectaveragePersonSalary = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println("员工总数:" + countPerson); // 员工总数:6
System.out.println("员工平均工资:" + averagePersonSalary); // 员工平均工资:9216.666666666666
System.out.println("员工工资总和:" + maxPersonSalary); // 员工工资总和:Optional[10500]
System.out.println("员工工资所有统计:" + collectaveragePersonSalary); // 员工工资所有统计:DoubleSummaryStatistics{count=6, sum=55300.000000, min=8000.000000, average=9216.666667, max=10500.000000}
// 接合(joining)
String names = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
System.out.println("所有员工的姓名:" + names); // 所有员工的姓名:Tom,Jack,Lily,Anni,Owen,Alisa
String string = stringList.stream().collect(Collectors.joining("-"));
System.out.println("拼接后的字符串:" + string); // 拼接后的字符串:adnm-admmt-pot-xbangd-weoujgsd
// concat:合并两个流 distinct:去重
String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
List<String> newConcat = Stream.concat(Stream.of(arr1), Stream.of(arr2)).distinct().collect(Collectors.toList());
System.out.println("流合并:" + newConcat); // 流合并:[a, b, c, d, e, f, g]
}
}
分组
- partitioningBy:分区:将stream按条件分为两个Map
- groupingBy:分组:将集合分为多个Map
public class StreamDemo {
public static void main(String[] args) {
// 将员工按薪资是否高于8000分为两部分;将员工按性别和地区分组
// 将员工按薪资是否高于8000分组
Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
// 将员工按性别分组
Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
// 将员工先按性别分组,再按地区分组
Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
System.out.println("员工按薪资是否大于8000分组情况:" + part); // 员工按薪资是否大于8000分组情况:{false=[Person(name=Jack, salary=8000, age=23, sex=male, area=Washington)], true=[Person(name=Tom, salary=9900, age=23, sex=male, area=New York), Person(name=Lily, salary=8800, age=24, sex=female, area=Washington), Person(name=Anni, salary=9200, age=24, sex=female, area=New York), Person(name=Owen, salary=10500, age=55, sex=male, area=New York), Person(name=Alisa, salary=8900, age=67, sex=female, area=New York)]}
System.out.println("员工按性别分组情况:" + group); // 员工按性别分组情况:{female=[Person(name=Lily, salary=8800, age=24, sex=female, area=Washington), Person(name=Anni, salary=9200, age=24, sex=female, area=New York), Person(name=Alisa, salary=8900, age=67, sex=female, area=New York)], male=[Person(name=Tom, salary=9900, age=23, sex=male, area=New York), Person(name=Jack, salary=8000, age=23, sex=male, area=Washington), Person(name=Owen, salary=10500, age=55, sex=male, area=New York)]}
System.out.println("员工按性别、地区:" + group2); // 员工按性别、地区:{female={New York=[Person(name=Anni, salary=9200, age=24, sex=female, area=New York), Person(name=Alisa, salary=8900, age=67, sex=female, area=New York)], Washington=[Person(name=Lily, salary=8800, age=24, sex=female, area=Washington)]}, male={New York=[Person(name=Tom, salary=9900, age=23, sex=male, area=New York), Person(name=Owen, salary=10500, age=55, sex=male, area=New York)], Washington=[Person(name=Jack, salary=8000, age=23, sex=male, area=Washington)]}}
}
}
排序
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
public class StreamDemo {
public static void main(String[] args) {
// 将员工按工资由高到低(工资一样则按年龄由大到小)排序
// 按工资升序排序(自然排序)
List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
// 按工资倒序排序
List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
// 先按工资再按年龄升序排序
List<String> newList3 = personList.stream()
.sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
.collect(Collectors.toList());
// 先按工资再按年龄自定义排序(降序)
List<String> newList4 = personList.stream().sorted((p1, p2) -> {
if (p1.getSalary() == p2.getSalary()) {
return p2.getAge() - p1.getAge();
} else {
return p2.getSalary() - p1.getSalary();
}
}).map(Person::getName).collect(Collectors.toList());
System.out.println("按工资升序排序:" + newList); // 按工资升序排序:[Jack, Lily, Alisa, Anni, Tom, Owen]
System.out.println("按工资降序排序:" + newList2); // 按工资降序排序:[Owen, Tom, Anni, Alisa, Lily, Jack]
System.out.println("先按工资再按年龄升序排序:" + newList3); // 先按工资再按年龄升序排序:[Jack, Lily, Alisa, Anni, Tom, Owen]
System.out.println("先按工资再按年龄自定义降序排序:" + newList4); // 先按工资再按年龄自定义降序排序:[Owen, Tom, Anni, Alisa, Lily, Jack]
}
}