jdk-8新特性
Lambda表达式
这个大家应该都会用,就不多赘述了
在jdk1.8 中为接口增强,提供了静态方法和默认方法 用于扩展方便
函数式接口
这里我们需要知道一个注解**@FunctionalInterface** 一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。不过,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽
象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
常用内置函数式接口介绍
它们主要在 java.util.function 包中。下面是最常用的几个接口。
- Supplier接口
@FunctionalInterface
public interface Supplier<T> {
public abstract T get();
}
java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类
型的对象数据。
供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口。
-
Consumer接口
@FunctionalInterface public interface Consumer<T> { public abstract void accept(T t); }
java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛
型参数决定。
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操
作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。
-
Function接口
@FunctionalInterface public interface Function<T, R> { public abstract R apply(T t); }
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,
后者称为后置条件。有参数有返回值。
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口
-
Predicate接口
@FunctionalInterface public interface Predicate<T> { public abstract boolean test(T t); } Predicate接口用于做判断,返回boolean类型的值
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用
java.util.function.Predicate 接口。
方法引用
双冒号 :: 写法,这被称为“方法引用”,是一种新的语法。
符号说明 : 双冒号为方法引用运算符,而它所在的表达式被称为方法引用。
应用场景 : 如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用。
常见引用方式
方法引用在JDK 8中使用方式相当灵活,有以下几种形式:
-
instanceName::methodName 对象::方法名
-
ClassName::staticMethodName 类名::静态方法
-
ClassName::methodName 类名::普通方法
-
ClassName::new 类名::new 调用的构造器
-
TypeName[]::new String[]::new 调用数组的构造器
对象名::引用成员方法
这是最常见的一种用法,与上例相同。如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法,代
码为:
// 对象::实例方法
@Test
public void test01() {
Date now = new Date();
Supplier<Long> supp = () -> {
return now.getTime();
};
System.out.println(supp.get());
Supplier<Long> supp2 = now::getTime;
System.out.println(supp2.get());
}
方法引用的注意事项
-
被引用的方法,参数要和接口中抽象方法的参数一样
-
当接口抽象方法有返回值时,被引用的方法也必须有返回值
类名::引用静态方法
由于在 java.lang.System 类中已经存在了静态方法 currentTimeMillis ,所以当我们需要通过Lambda来调用该
方法时,可以使用方法引用 , 写法是:类名::静态方法
@Test
public void test02() {
Supplier<Long> supp = () -> {
return System.currentTimeMillis();
};
System.out.println(supp.get());
Supplier<Long> supp2 = System::currentTimeMillis;
System.out.println(supp2.get());
}
类名::引用实例方法
Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数作为方法的调用
者。
// 类名::实例方法
@Test
public void test03() {
Function<String, Integer> f1 = (s) -> {
return s.length();
};
System.out.println(f1.apply("abc"));
Function<String, Integer> f2 = String::length;
System.out.println(f2.apply("abc"));
BiFunction<String, Integer, String> bif = String::substring;
String hello = bif.apply("hello", 2);
System.out.println("hello = " + hello);
}
类名::new引用构造器
由于构造器的名称与类名完全一样。所以构造器引用使用 类名称::new 的格式表示。首先是一个简单的 Person 类:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// 类名::new
@Test
public void test04() {
Supplier<Person> sup = () -> {
return new Person();
};
System.out.println(sup.get());
Supplier<Person> sup2 = Person::new;
System.out.println(sup2.get());
BiFunction<String, Integer, Person> fun2 = Person::new;
System.out.println(fun2.apply("张三", 18));
}
数组::new引用数组构造器
数组也是 Object 的子类对象,所以同样具有构造器,只是语法稍有不同。
// 类型[]::new
@Test
public void test05() {
Function<Integer, String[]> fun = (len) -> {
return new String[len];
};
String[] arr1 = fun.apply(10);
System.out.println(arr1 + ", " + arr1.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] arr2 = fun.apply(5);
System.out.println(arr2 + ", " + arr2.length);
}
方法引用是对Lambda表达式符合特定情况下的一种缩写,它使得我们的Lambda表达式更加的精简,也可以理解为Lambda表达式的缩写形式 , 不过要注意的是方法引用只能"引用"已经存在的方法
stream流
今天来总结下jdk8的新特性stream流,这个是很久以前我就系统性的学过了,并也实际在项目中运用过,今天太闲了,再总结下_我也顺便再复习一下~~~
友情提示:这个大家用过的就赶紧去学一下吧,你会发现会stream流真的太好用了 嘻嘻嘻嘻嘻嘻!!!
上干货!!!
Stream是流式思想,相当于工厂的流水线,对集合中的数据进行加工处理
掌握根据Collection获取流
掌握Stream中的静态方法of获取流
java.util.stream.Stream 是JDK 8新加入的流接口。
获取一个流非常简单,有以下几种常用的方式:
-
所有的 Collection 集合都可以通过 stream 默认方法获取流;
-
Stream 接口的静态方法 of 可以获取数组对应的流。
方式一:Collection 集合都可以通过 stream 默认方法获取流;
java.util.Map 接口不是 Collection 的子接口,所以获取对应的流需要分key、value或entry等情况
public static void main(String[] args) {
// Map获取流
Map<String, String> map = new HashMap<>();
// ...
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}
方式2 : Stream中的静态方法 of 获取流
由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单
String[] arr = {"aa", "bb", "cc"};
Stream<String> stream7 = Stream.of(arr);
- 备注: of 方法的参数其实是一个可变参数,所以支持数组。
学习了两种获取流的方式:
-
通过Collection接口中的默认方法Stream stream()
-
通过Stream接口中的静态of方法
Stream常用方法
Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
中间操作符、终止操作符
中间操作符
流方法 | 含义 | 示例 |
---|---|---|
filter | 用于通过设置的条件过滤出元素 | List strings = Arrays.asList(“abc”, “”, “bc”, “efg”, “abcd”,“”, “jkl”);List filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); |
distinct | 返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流。 | List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println); |
limit | 会返回一个不超过给定长度的流。 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”);List limited = strings.stream().limit(3).collect(Collectors.toList()); |
skip | 返回一个扔掉了前n个元素的流。 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”);List skiped = strings.stream().skip(3).collect(Collectors.toList()); |
map | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)。 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”);List mapped = strings.stream().map(str->str+“-itcast”).collect(Collectors.toList()); |
flatMap | 使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流。 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”);Stream flatMap = strings.stream().flatMap(Java8StreamTest::getCharacterByString); |
sorted | 返回排序后的流 | List strings1 = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);List sorted1 = strings1.stream().sorted().collect(Collectors.toList()); |
终止操作符
流方法 | 含义 | 示例 |
---|---|---|
anyMatch | 检查是否至少匹配一个元素,返回boolean。 | List strings = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);boolean b = strings.stream().anyMatch(s -> s == “abc”); |
allMatch | 检查是否匹配所有元素,返回boolean。 | List strings = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);boolean b = strings.stream().allMatch(s -> s == “abc”); |
noneMatch | 检查是否没有匹配所有元素,返回boolean。 | List strings = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);boolean b = strings.stream().noneMatch(s -> s == “abc”); |
findAny | 将返回当前流中的任意元素。 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);Optional any = strings.stream().findAny(); |
findFirst | 返回第一个元素 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);Optional first = strings.stream().findFirst(); |
forEach | 遍历流 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);strings.stream().forEach(s -> out.println(s)); |
collect | 收集器,将流转换为其他形式。 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);Set set = strings.stream().collect(Collectors.toSet());List list = strings.stream().collect(Collectors.toList());Map<String, String> map = strings.stream().collect(Collectors.toMap(v ->v.concat(“_name”), v1 -> v1, (v1, v2) -> v1)); |
reduce | 可以将流中元素反复结合起来,得到一个值。 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);Optional reduce = strings.stream().reduce((acc,item) -> {return acc+item;});if(reduce.isPresent())out.println(reduce.get()); |
count | 返回流中元素总数。 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);long count = strings.stream().count(); |
Stream的3个注意事项:
-
Stream只能操作一次
-
Stream方法返回的是新的流
-
Stream不调用终结方法,中间的操作不会执行
Stream流的conca方法
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :
static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
备注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。
该方法的基本使用代码如:
@Test
public void testContact() {
Stream<String> streamA = Stream.of("张三");
Stream<String> streamB = Stream.of("李四");
Stream<String> result = Stream.concat(streamA, streamB);
result.forEach(System.out::println);
}
Stream流中的结果到集合中
Stream流提供 collect 方法,其参数需要一个 java.util.stream.Collector<T,A, R> 接口对象来指定收集到哪
种集合中。java.util.stream.Collectors 类提供一些方法,可以作为 Collector`接口的实例:
-
public static Collector<T, ?, List> toList() :转换为 List 集合。
-
public static Collector<T, ?, Set> toSet() :转换为 Set 集合。
Stream流中的结果到数组中
Stream提供 toArray 方法来将结果放到一个数组中,返回值类型是Object[]的:
Object[] toArray()
对流中数据进行聚合计算
当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作。比如获取最大值,获取最小值,求总和,平均值,统计数量。
@Test
public void testStreamToOther() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 58, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// 获取最大值
// Optional<Student> collect = studentStream.collect(Collectors.maxBy((o1, o2) ->
o1.getSocre() - o2.getSocre()));
// 获取最小值
// Optional<Student> collect = studentStream.collect(Collectors.minBy((o1, o2) ->
o1.getSocre() - o2.getSocre()));
// System.out.println(collect.get());
// 求总和
// int sumAge = studentStream.collect(Collectors.summingInt(s -> s.getAge()));
// System.out.println("sumAge = " + sumAge);
// 平均值
// double avgScore = studentStream.collect(Collectors.averagingInt(s -> s.getSocre()));
// System.out.println("avgScore = " + avgScore);
// 统计数量
// Long count = studentStream.collect(Collectors.counting());
// System.out.println("count = " + count);
}
对流中数据进行分组
当我们使用Stream流处理数据后,可以根据某个属性将数据分组:
// 分组
@Test
public void testGroup() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
// Map<Integer, List<Student>> map =
studentStream.collect(Collectors.groupingBy(Student::getAge));
// 将分数大于60的分为一组,小于60分成另一组
Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy((s) ->
{ if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}}));
map.forEach((k, v) -> {
System.out.println(k + "::" + v);
});
}
多级分组
@Test
public void testCustomGroup () {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
Map<Integer, Map<String, List<Student>>> map =
studentStream.collect(Collectors.groupingBy(s -> s.getAge(), Collectors.groupingBy(s -> {
if (s.getSocre() >= 90) {
return "优秀";
} else if (s.getSocre() >= 80 && s.getSocre() < 90) {
return "良好";
} else if (s.getSocre() >= 80 && s.getSocre() < 80) {
return "及格";
} else {
return "不及格";
}
})));
map.forEach((k, v) -> {
System.out.println(k + " == " + v);
});
}
分区
// 分区
@Test
public void testPartition() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
// partitioningBy会根据值是否为true,把集合分割为两个列表,一个true列表,一个false列表。
Map<Boolean, List<Student>> map = studentStream.collect(Collectors.partitioningBy(s ->
s.getSocre() > 90));
map.forEach((k, v) -> {
System.out.println(k + " == " + v);
});
}
对流中数据进行拼接
Collectors.joining 会根据指定的连接符,将所有元素连接成一个字符串。
@Test
public void testJoining() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
String collect = studentStream
.map(Student::getName)
.collect(Collectors.joining(">_<", "^_^", "^v^"));
System.out.println(collect);
}
}
小结
收集Stream流中的结果
-
到集合中: Collectors.toList()/Collectors.toSet()/Collectors.toCollection()
-
到数组中: toArray()/toArray(int[]::new)
-
聚合计算:
Collectors.maxBy/Collectors.minBy/Collectors.counting/Collectors.summingInt/Collectors.averagingInt
-
分组: Collectors.groupingBy
-
分区: Collectors.partitionBy
-
拼接: Collectors.joinging