更新于 2022-6-1 15:32
文章目录
简介
速度更快
代码更少(增加了新的语法Lambda表达式)
强大的Stream API
便于并行
最大化减少空指针异常Optional
lambda表达式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码" + Thread.currentThread().getName());
}
}).start();
//开启一个新线程
new Thread(() -> System.out.println("新线程中执行的代码"+Thread.currentThread().getName()));
简化了代码 ->
lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成
(参数类型 参数名称) -> {
代码体;
}
lambda练习一:
先创建一个UserService接口 无返回的方法
public interface UserService {
/**
* wwu
*/
void show();
}
然后
public static void main(String[] args) {
goShow(new UserService() {
@Override
public void show() {
System.out.println("显示");
}
});
goShow(() -> System.out.println("Hello"));
}
public static void goShow(UserService userService){
userService.show();
}
lambda练习二:
先创建一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Integer height;
}
然后
public class Demo04 {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("周杰伦",18,180));
list.add(new Person("刘德华",35,180));
list.add(new Person("张学友",21,180));
list.add(new Person("周星驰",25,180));
list.add(new Person("周润发",31,180));
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
for(Person person : list){
System.out.println(person);
}
System.out.println("------------");
list.sort(Comparator.comparingInt(Person::getAge));
list.forEach(System.out::println);
}
}
省略写法
先写两个接口
public interface OrderService {
Integer show(String name);
}
public interface StudentService {
String show(String name,Integer age);
}
然后
public class Demo05 {
public static void main(String[] args) {
goStudent((String name,Integer age)->{
return name + age +"666...";
});
// 省略写法
goStudent((name, age) ->
name+age+"666"
);
System.out.println("--------");
geOrder((String name)->{
System.out.println("--->" + name);
return 999;
});
// 省略写法
geOrder(name -> 999);
}
public static void goStudent(StudentService service){
service.show("张三",22);
}
public static void geOrder(OrderService orderService){
orderService.show("李四");
}
}
接口中新增的方法
1.JDK8中接口的新增
在JDK8中针对接口有增强,在JDK8之前:
interface 接口名{
静态常量;
抽象方法;
}
JDK8之后对接口做了增强,接口中可以有默认方法和静态方法
interface 接口名{
静态常量;
抽象方法;
默认方法;
静态方法;
}
2.默认方法
interface A {
void test1();
void test2();
}
class B implements A {
@Override
public void test1() {
}
@Override
public void test2() {
}
}
接口新增方法,实现的类也要去实现方法
不方便,麻烦
接口默认方法的格式
interface 接口名{
修饰符 default 返回值类型 方法名{
}
}
接口中默认方法的使用
1.实现类直接调用接口的默认方法
2.实现类重写接口的默认方法
public class Demo01 {
public static void main(String[] args) {
A a = new B();
A c = new C();
// 实现类重写接口的默认方法
a.test3();
// 实现类直接调用接口的默认方法
c.test3();
}
}
interface A {
void test1();
void test2();
/**
* 接口中定义的默认方法
* @return
*/
public default String test3(){
System.out.println("接口中的默认方法执行了。。。。");
return "hello";
}
}
class B implements A {
@Override
public void test1() {
}
@Override
public void test2() {
}
@Override
public String test3() {
System.out.println("B 实现类中重写了默认方法。。");
return "ok...";
}
}
class C implements A {
@Override
public void test1() {
}
@Override
public void test2() {
}
}
3.静态方法
JDK8中为接口新增了静态方法,作用也是为了接口的扩展
语法规则
interface 接口名{
修饰符 static 返回值类型 方法名{
方法体;
}
}
接口中静态方法的使用
public static String test4(){
System.out.println("接口中的静态方法。。。");
return "hello";
}
接口中的静态方法是不能够被重写的。
两者的区别介绍
1.默认方法通过实例调用,静态方法通过接口名调用
2.默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
3.静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用
函数式接口
函数式接口的由来
使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,只关心参数列表和返回值类型,为了使用Lambda表达式更加方便,在JDK8中提供了大量的常用函数式接口
public class Demo {
public static void main(String[] args) {
fun1(arr -> {
int sum = 0;
for (int i : arr) {
sum += i;
}
return sum;
});
}
public static void fun1(Operator operator){
int[] arr = {1,2,3,4};
int sum = operator.getSum(arr);
System.out.println("sum = " + sum);
}
}
/**
* 函数式接口
*/
@FunctionalInterface
interface Operator{
int getSum(int[] arr);
}
接口介绍
Supplier
@FunctionalInterface public interface Supplier<T> { T get(); }
无参有返回值的接口。
使用可以省去定义一个外部函数式接口。
定义一个方法来试一下,比如求个数组中最小值。
/**
* @author Lenovo
*/
public class SupplierTest {
public static void main(String[] args) {
int[] arr = {1,4,6,2,454,23423,12,35};
fun(() -> {
Arrays.sort(arr);
return arr[0];
});
}
private static void fun(Supplier<Integer> supplier){
Integer min = supplier.get();
System.out.println("supplier接口函数--数组中的最小值:" + min);
}
}
输出结果
supplier接口函数--数组中的最小值:1
Consumer
@FunctionalInterface public interface Consumer<T> { void accept(T t);}
有参无返回值的接口。
Supplier接口用来产生数据,而Consumer是用来消耗数据的,对数据进行操作。
让我们来做个小实验,把字符串转为大写。
/**
* @author Lenovo
*/
public class ConsumerTest {
public static void main(String[] args) {
fun(s -> {
String s1 = s.toUpperCase(Locale.ROOT);
System.out.println(s + "-->大写转化-->" + s1);
});
}
private static void fun(Consumer<String> consumer){
String str = "hello";
consumer.accept(str);
}
}
输出结果
hello-->大写转化-->HELLO
接下来介绍一下默认方法andThen,andThen可以控制两个参数的前后执行顺序。
default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
/**
* @author Lenovo
*/
public class ConsumerTest {
public static void main(String[] args) {
fun(consumer -> {
System.out.println(consumer + "-->大写转化-->" + consumer.toUpperCase());
},consumer2 -> {
System.out.println(consumer2 + "-->小写转化-->" + consumer2.toLowerCase());
});
}
private static void fun(Consumer<String> consumer,Consumer<String> consumer2){
String str = "Hello World";
consumer.accept(str);
consumer2.accept(str);
System.out.println("--------");
consumer.andThen(consumer2).accept(str);
System.out.println("---------");
consumer2.andThen(consumer).accept(str);
}
}
输出结果
Hello World-->大写转化-->HELLO WORLD
Hello World-->小写转化-->hello world
--------
Hello World-->大写转化-->HELLO WORLD
Hello World-->小写转化-->hello world
---------
Hello World-->小写转化-->hello world
Hello World-->大写转化-->HELLO WORLD
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; } }
有参有返回值的接口。
R apply(T t);
输入一个T类型,返回一个R类型,做个测试
String转Integer
/**
* @author Lenovo
*/
public class FunctionTest {
public static void main(String[] args) {
test(Integer::parseInt);
}
private static void test(Function<String,Integer> function){
Integer apply = function.apply("123");
System.out.println("apply==" + apply);
}
}
输出结果
apply==123
默认方法andThen
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
这个方法和上面的同名方法差不多,就不过多介绍了,compose 刚好顺序和它相反
Predicate
@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); } }
boolean test(T t);
第一个为有参,返回Boolea类型。
/**
* @author Lenovo
*/
public class PredicateTest {
public static void main(String[] args) {
test(s -> s.length()>3);
}
private static void test(Predicate<String> predicate){
boolean b = predicate.test("hello");
System.out.println("b:" + b);
}
}
返回结果:true
做个多方法实验
/**
* @author Lenovo
*/
public class PredicateTest {
public static void main(String[] args) {
test(p1 -> p1.contains("h"),
p2 -> p2.contains("w")
);
}
private static void test(Predicate<String> p1, Predicate<String> p2) {
// p1 和 p2 条件同时满足
boolean b1 = p1.and(p2).test("hello");
// p1 和 p2 满足一个条件
boolean b2 = p1.or(p2).test("hello");
// p1 的条件否定
boolean b3 = p1.negate().test("hello");
System.out.println(b1); // false
System.out.println(b2); // true
System.out.println(b3); // false
}
}
方法引用与构造器引用
Stream API
1.集合处理数据的弊端
复杂麻烦,反复循环
public class Demo {
public static void main(String[] args) {
List<String> list = Arrays.asList("张三", "李四", "张三丰", "牛逼啊");
//获取姓张的信息
List<String> list1 = new ArrayList<>();
list.forEach(s -> {if (s.startsWith("张")){
list1.add(s);
}});
//获取名称长度为3的用户
List<String> list2 = new ArrayList<>();
list.forEach(s -> {
if (s.length()==3){
list2.add(s);
}
});
//输出所有的用户信息
list.forEach(System.out::println);
}
}
使用Stream解决以上繁琐
public class Demo1 {
public static void main(String[] args) {
List<String> list = Arrays.asList("张三", "李四", "张三丰", "牛逼啊");
//获取姓张的信息
//获取名称长度为3的用户
//输出所有的用户信息
list.stream().filter(s -> s.startsWith("张"))
.filter(s -> s.length()==3)
.forEach(System.out::print);
}
}
核心思想
不存储数据,不是一种数据结构。
Stream的两种获取方式
根据Collection获取
java.util.Collection的Stream
public class Demo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.stream();
Set<String> set = new HashSet<>();
set.stream();
Vector<String> strings = new Vector<>();
strings.stream();
}
}
map没有直接获取Stream的方法,可以这样:
public class Demo3 {
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
// 通过key获取
Stream<String> stream = map.keySet().stream();
// 通过值获取
Stream<Object> stream1 = map.values().stream();
// 通过entry获取
Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream();
}
}
通过Stream的of方法获取
在实际开发中我们会操作到数组,由于数组对象不可以直接转化为Steam流,所以
public class Demo4 {
public static void main(String[] args) {
Stream<String> a1 = Stream.of("a1","a2","a3");
String [] arr1 = {"aa","bb","cc"};
Stream<String> arr11 = Stream.of(arr1);
Integer[] arr2 = {1,2,3,4,5,6};
Stream<Integer> arr22 = Stream.of(arr2);
// 基本类型不能直接转化
int [] arr3 = {1,3,3,53,6};
Stream<int[]> arr31 = Stream.of(arr3);
arr31.forEach(System.out::println);
}
}
Stream常用方法介绍
注意事项
1.Stream只能操作一次。
2.Stream方法返回的是新的流
3.Stream不调用终结方法(Count、foreach etc..),中间的操作不会执行
forEach
遍历流中的数据
Integer[] arr2 = {1,2,3,4,5,6};
Stream.of(arr2).forEach(System.out::println);
count
Integer[] arr2 = {1,2,3,4,5,6};
System.out.println(Stream.of(arr2).count());
filter
List<String> list = Arrays.asList("张三", "李四", "张三丰", "牛逼啊");
//获取姓张的信息
//获取名称长度为3的用户
//输出所有的用户信息
list.stream().filter(s -> s.startsWith("张"))
.filter(s -> s.length()==3)
.forEach(System.out::print);
limit
截取limit长度的数据,其他不要
list.stream().limit(3).forEach(System.out::print);
输出前三个
skip
跳过几个元素
list.stream().skip(3).forEach(System.out::print);
map
转化元素
Stream.of("1","2","3","4","5","6")
// .map(s -> Integer.valueOf(s))
.map(Integer::valueOf)
.forEach(System.out::print); //123456
sorted
排序用
Stream.of("1","6","9","4","5","6")
// .map(s -> Integer.valueOf(s))
.map(Integer::valueOf)
.sorted()
.forEach(System.out::print);// 145669
.sorted((o1, o2) -> o2-o1)
distinct
去掉重复数据,对象集合也可以使用。
.distinct()
match
判断 第一个false 第二个true
List<Employee> list2 = Arrays.asList(
new Employee(1, "Alex", 1000),
new Employee(2, "Michael", 2000),
new Employee(3, "Jack", 1500),
new Employee(4, "Owen", 1500),
new Employee(5, "Denny", 2000));
boolean b = list2.stream().allMatch(employee -> employee.getMoney() > 1000);
System.out.println(b);
boolean b1 = list2.stream().anyMatch(employee -> employee.getMoney() > 1000);
System.out.println(b1);
find
findAny返回任何一个元素,在串行流中返回第一个元素,在并行流中返回处理最快那个线程的元素。
Optional<Employee> any = list2.stream().findAny();
System.out.println(any);
findFirst()返回第一个元素,在串行流和并行流中均返回第一个元素。
Optional<Employee> any = list2.stream().findFirst();
System.out.println(any);
max和min
最大和最小元素
Optional<Employee> max = list2.stream().max(Comparator.comparingDouble(Employee::getMoney));
System.out.println(max);
reduce
Integer sum = Stream.of(4,5,6,3,1)
// identity 默认值
// 第一次默认值会赋值给x
// 之后每次会将 上一次的操作结果赋值给x y就是每次从数据中获取的元素
.reduce(0,(x,y)->{
System.out.println("x="+x + "y=" +y);
return x +y;
});
System.out.println(sum);
System.out.println("----------------");
Integer max = Stream.of(4,5,6,4,3)
.reduce(0,(x,y)->{
return x>y ? x:y;
});
System.out.println(max);
输出
x=0y=4
x=4y=5
x=9y=6
x=15y=3
x=18y=1
19
----------------
6
System.out.println(Stream.iterate(0, x -> x + 1).limit(10).reduce(Integer::sum).orElse(0)); // 45
System.out.println(Stream.iterate(0, x -> x + 1).limit(10).reduce(1000, Integer::sum)); // 1045
System.out.println(Stream.iterate(0, x -> x + 1).limit(10).reduce(1000, Integer::sum, Integer::max)); // 1045
map和reduce的组合
List<Employee> list2 = Arrays.asList(
new Employee(1, "Alex", 1000),
new Employee(2, "Michael", 2000),
new Employee(3, "Jack", 1500),
new Employee(4, "Owen", 1500),
new Employee(5, "Denny", 2000));
// 求所有余额的和
Integer reduce = list2.stream().map(Employee::getMoney)
.reduce(0, Integer::sum);
System.out.println(reduce);
// 求所有余额的最大值
Integer reduce2 = list2.stream().map(Employee::getMoney)
.reduce(0, Integer::max);
System.out.println(reduce2);
// 统计字符a出现的次数
Integer count = Stream.of("a","b","a","c","a","e")
.map(ch->"a".equals(ch) ? 1:0)
.reduce(0,Integer::sum);
System.out.println(count); // 3
mapToInt(mapToDoub,mapToLong)
IntStream intStream = Stream.of(arr) .mapToInt(Integer::intValue); System.out.println(intStream);
concat
拼接作用 合成一个流
public static void main(String[] args) {
System.out.println("输出结果");
List<String> list1 = Arrays.asList("迪丽热巴","宋远桥","陈道明","陈美嘉","陆子桥","张晓明");
List<String> list2 = Arrays.asList("杨颖","张三","宋钟基","卢布","刘备","曹贼","刘关张");
Stream<String> stream1 = list1.stream().filter(s -> s.length()==3);
Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("刘"));
Stream.concat(stream1,stream2)
.map(Employee::new)
.forEach(System.out::println);
}
输出结果
Employee(id=null, name=宋远桥, money=null)
Employee(id=null, name=陈道明, money=null)
Employee(id=null, name=陈美嘉, money=null)
Employee(id=null, name=陆子桥, money=null)
Employee(id=null, name=张晓明, money=null)
Employee(id=null, name=刘备, money=null)
Employee(id=null, name=刘关张, money=null)
综合案例
public class Demo1 {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("迪丽热巴","宋远桥","陈道明","陈美嘉","陆子桥","张晓明");
List<String> list2 = Arrays.asList("杨颖","张三","宋钟基","卢布","刘备","曹贼","刘关张");
Stream<String> stream1 = list1.stream().filter(s -> s.length()==3);
Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("刘"));
Stream.concat(stream1,stream2)
.map(Employee::new)
.forEach(System.out::println);
}
}
@NoArgsConstructor
@AllArgsConstructor
@Data
class Employee {
private Integer id;
private String name;
private Integer money;
public Employee(String name) {
this.name = name;
}
}
输出结果
Employee(id=null, name=宋远桥, money=null)
Employee(id=null, name=陈道明, money=null)
Employee(id=null, name=陈美嘉, money=null)
Employee(id=null, name=陆子桥, money=null)
Employee(id=null, name=张晓明, money=null)
Employee(id=null, name=刘备, money=null)
Employee(id=null, name=刘关张, money=null)
结果收集
结果收集到集合
@Test
public void test01(){
// 收集到列表中
List<String> collect = Stream.of("aa", "bb", "cc","aa")
.collect(Collectors.toList());
System.out.println(collect);
// 收集到集合中
Set<String> set = Stream.of("aa", "bb", "cc", "aa")
.collect(Collectors.toSet());
System.out.println(set);
// 指定要获取的类具体实现 比如ArrayList HashSet
ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(arrayList);
HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa")
.collect(Collectors.toCollection(HashSet::new));
System.out.println(hashSet);
}
输出结果:
[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]
结果收集到数组
/**
* Stream 结果收集到数组中
*/
@Test
public void test02(){
// 返回Object类型数组
Object[] objects = Stream.of("aa", "bb", "cc", "aa")
.toArray();
System.out.println(Arrays.toString(objects));
String[] strings = Stream.of("aa", "bb", "cc", "aa")
.toArray(String[]::new);
System.out.println(Arrays.toString(strings));
}
对流中的数据做聚合计算(最大值、求和、平均值等)
/**
* Stream 流做聚合计算
*/
@Test
public void test03() {
// 获取年龄最大值
Optional<Employee> max = Stream.of(
new Employee(1, "王聪祥", 15),
new Employee(2, "陈晓明", 23),
new Employee(3, "关羽", 28),
new Employee(4, "张飞", 19),
new Employee(5, "诸葛亮", 43)
).max(Comparator.comparingInt(Employee::getMoney));
System.out.println("最大年龄:" + max.get());
Optional<Employee> min = Stream.of(
new Employee(1, "王聪祥", 15),
new Employee(2, "陈晓明", 23),
new Employee(3, "关羽", 28),
new Employee(4, "张飞", 19),
new Employee(5, "诸葛亮", 43)
).min(Comparator.comparingInt(Employee::getMoney));
System.out.println("最小年龄:" + min);
Integer collect = Stream.of(
new Employee(1, "王聪祥", 15),
new Employee(2, "陈晓明", 23),
new Employee(3, "关羽", 28),
new Employee(4, "张飞", 19),
new Employee(5, "诸葛亮", 43)
).mapToInt(Employee::getMoney).sum();
System.out.println("总和:" + collect);
Double collect1 = Stream.of(
new Employee(1, "王聪祥", 15),
new Employee(2, "陈晓明", 23),
new Employee(3, "关羽", 28),
new Employee(4, "张飞", 19),
new Employee(5, "诸葛亮", 43)
).collect(Collectors.averagingInt(Employee::getMoney));
System.out.println("平均值:" + collect1);
long count = Stream.of(
new Employee(1, "王聪祥", 15),
new Employee(2, "陈晓明", 23),
new Employee(3, "关羽", 28),
new Employee(4, "张飞", 19),
new Employee(5, "诸葛亮", 43)
).count();
System.out.println("统计数量: " + count);
}
输出结果:
最大年龄:Employee(id=5, name=诸葛亮, money=43)
最小年龄:Optional[Employee(id=1, name=王聪祥, money=15)]
总和:128
平均值:25.6
统计数量: 5
分组计算
@Test
public void test04(){
// 根据姓名进行分组
Map<String, List<Employee>> ma = Stream.of(
new Employee(1, "王聪祥", 1235334),
new Employee(2, "诸葛亮", 1535),
new Employee(3, "关羽", 2438),
new Employee(4, "王聪祥", 5000),
new Employee(5, "诸葛亮", 43)
).collect(Collectors.groupingBy(Employee::getName));
ma.forEach((s, employees) -> System.out.println("key=" + s + "\tvalues=" + employees));
}
输出结果:
key=关羽 values=[Employee(id=3, name=关羽, money=2438)]
key=诸葛亮 values=[Employee(id=2, name=诸葛亮, money=1535), Employee(id=5, name=诸葛亮, money=43)]
key=王聪祥 values=[Employee(id=1, name=王聪祥, money=1235334), Employee(id=4, name=王聪祥, money=5000)]
@Test
public void test05(){
// 先根据姓名,再根据余额(小康或者穷逼)进行分组
Map<String, Map<String, List<Employee>>> collect = Stream.of(
new Employee(1, "王聪祥", 1235334),
new Employee(2, "诸葛亮", 1535),
new Employee(3, "关羽", 2438),
new Employee(4, "王聪祥", 5000),
new Employee(5, "诸葛亮", 43)
).collect(Collectors.groupingBy(Employee::getName,
Collectors.groupingBy(e -> e.getMoney() >= 3000 ? "有钱" : "穷逼")));
collect.forEach((s, integerListMap) -> System.out.println("key=" + s + "\tvalues=" + integerListMap));
}
输出结果:
key=关羽 values={穷逼=[Employee(id=3, name=关羽, money=2438)]}
key=诸葛亮 values={穷逼=[Employee(id=2, name=诸葛亮, money=1535), Employee(id=5, name=诸葛亮, money=43)]}
key=王聪祥 values={有钱=[Employee(id=1, name=王聪祥, money=1235334), Employee(id=4, name=王聪祥, money=5000)]}
对流中的数据做分区操作
对流中的数据做两个划分,条件达成为true,不达成则是false
Stream.of(
new Employee(1, "王聪祥", 1235334),
new Employee(2, "诸葛亮", 1535),
new Employee(3, "关羽", 2438),
new Employee(4, "王聪祥", 5000),
new Employee(5, "诸葛亮", 43)
).collect(Collectors.partitioningBy(employee -> employee.getMoney()>3000))
.forEach((s, integerListMap) -> System.out.println("key=" + s + "\tvalues=" + integerListMap));
输出结果
key=false values=[Employee(id=2, name=诸葛亮, money=1535), Employee(id=3, name=关羽, money=2438), Employee(id=5, name=诸葛亮, money=43)]
key=true values=[Employee(id=1, name=王聪祥, money=1235334), Employee(id=4, name=王聪祥, money=5000)]
对流中的数据做拼接
String collect = Stream.of(
new Employee(1, "王聪祥", 1235334),
new Employee(2, "诸葛亮", 1535),
new Employee(3, "关羽", 2438),
new Employee(4, "王聪祥", 5000),
new Employee(5, "诸葛亮", 43)
).map(Employee::getName)
.collect(Collectors.joining());
System.out.println(collect);
String collect2 = Stream.of(
new Employee(1, "王聪祥", 1235334),
new Employee(2, "诸葛亮", 1535),
new Employee(3, "关羽", 2438),
new Employee(4, "王聪祥", 5000),
new Employee(5, "诸葛亮", 43)
).map(Employee::getName)
.collect(Collectors.joining("_"));
System.out.println(collect2);
String collect3 = Stream.of(
new Employee(1, "王聪祥", 1235334),
new Employee(2, "诸葛亮", 1535),
new Employee(3, "关羽", 2438),
new Employee(4, "王聪祥", 5000),
new Employee(5, "诸葛亮", 43)
).map(Employee::getName)
.collect(Collectors.joining("_", "###", "$$$"));
System.out.println(collect3);
输出结果
王聪祥诸葛亮关羽王聪祥诸葛亮
王聪祥_诸葛亮_关羽_王聪祥_诸葛亮
###王聪祥_诸葛亮_关羽_王聪祥_诸葛亮$$$
并行的Stream流
串行流
上面的Stream处理方式都是串行的方式,也都是用主线程来执行
/**
* 串行流
*/
@Test
public void test01(){
Stream.of(1,2,3,4,5,6,7,4,3,2,11)
.filter(s->{
System.out.println(Thread.currentThread()+ "--" + s);
return s > 3;
}).count();
}
输出结果
Thread[main,5,main]--1
Thread[main,5,main]--2
Thread[main,5,main]--3
Thread[main,5,main]--4
Thread[main,5,main]--5
Thread[main,5,main]--6
Thread[main,5,main]--7
Thread[main,5,main]--4
Thread[main,5,main]--3
Thread[main,5,main]--2
Thread[main,5,main]--11
创建并行流
parallelStream 并行流,它通过ForkjoinPool,可以提高多线程任务的速度。
两种方式创建并行流
/**
* 两种方式创建并行流
*/
@Test
public void test02(){
// 通过List 接口 直接获取并行流
List<Integer> list = new ArrayList<>();
Stream<Integer> integerStream = list.parallelStream();
// 通过串行流转为并行流
Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 5)
.parallel();
}
并行流操作
/**
* 并行流操作
*/
@Test
public void test03(){
Stream.of(1,3,4,5,6,3,9,12,11)
.parallel()
.filter(s->{
System.out.println(Thread.currentThread() + "---" + s);
return s>2;
}).count();
}
输出结果
Thread[ForkJoinPool.commonPool-worker-2,5,main]---12
Thread[main,5,main]---3
Thread[ForkJoinPool.commonPool-worker-6,5,main]---1
Thread[ForkJoinPool.commonPool-worker-1,5,main]---4
Thread[ForkJoinPool.commonPool-worker-4,5,main]---9
Thread[ForkJoinPool.commonPool-worker-7,5,main]---11
Thread[ForkJoinPool.commonPool-worker-3,5,main]---3
Thread[ForkJoinPool.commonPool-worker-5,5,main]---5
Thread[ForkJoinPool.commonPool-worker-2,5,main]---6
并行流比较串行流
通过for循环、串行和并行做比较。
private static long times = 500000000;
private long start;
@Before
public void before(){
start = System.currentTimeMillis();
}
@After
public void end(){
long end = System.currentTimeMillis();
System.out.println("消耗时间==" + (end-start));
}
/**
* 普通for 循环 消耗时间: 281
*/
@Test
public void test01(){
System.out.println("普通for循环");
long res = 0;
for (int i = 0; i < times; i++) {
res += i;
}
}
/**
* 串行流 消耗时间:313
*/
@Test
public void test02(){
System.out.println("串行流");
LongStream.rangeClosed(0,times)
.reduce(0,Long::sum);
}
/**
* 并行流 消耗时间:99
*/
@Test
public void test03(){
System.out.println("并行流");
LongStream.rangeClosed(0,times)
.parallel()
.reduce(0,Long::sum);
}
线程安全问题
多线程状态下肯定会有线程安全问题,如下:
/**
* 并行流中的数据安全问题
*/
@Test
public void test04(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
List<Integer> list1 = new ArrayList<>();
list.stream()
.parallel()
// .forEach(s-> New.add(s));
.forEach(list1::add);
System.out.println(list1.size());
}
java.lang.ArrayIndexOutOfBoundsException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
....
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
解决办法如下:
/**
* 第一种方法:
* 加同步锁
*/
@Test
public void test05(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
List<Integer> list1 = new ArrayList<>();
Object obj = new Object();
list.stream()
.parallel()
// .forEach(s-> New.add(s));
.forEach(s->{
synchronized (obj){
list1.add(s);
}
});
System.out.println(list1.size());
}
/**
* 第2种方法:
* 使用线程安全的容器
*/
@Test
public void test06(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
Vector<Integer> list1 = new Vector<>();
list.stream()
.parallel()
// .forEach(s-> New.add(s));
.forEach(list1::add);
System.out.println(list1.size());
}
/**
* 第3种方法:
* 使用Stream的 toArray或 collect 方法来操作
*/
@Test
public void test07(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
List<Integer> list1 = list.stream()
.parallel().collect(Collectors.toList());
System.out.println(list1.size());
Integer[] list2 = list.stream()
.parallel().toArray(Integer[]::new);
System.out.println(list2.length);
}
Fork/Join框架
parallerStream 使用的是Fork/Join框架,Fork/Join从jdk7引入,Fork/Join框架可以将一个大人物拆分为多个小任务来执行,Fork/Join主要包括三个模块:
1.线程池:ForkJoinPool
2.任务对象:ForkJoinTask
3.执行的线程:ForkJoinWorkerThread
Optional类
Optional类主要解决空指针问题
创建Optional
// 第一张方式 通过of方法 但是不支持null
Optional<String> op1 = Optional.of("张三");
// Optional<String> op2 = Optional.of(null);
// 第二种方式 通过ofNullable方法 支持null
Optional<String> op3 = Optional.ofNullable("李四");
Optional<String> op4 = Optional.ofNullable(null);
// 第三种方式 通过empty方法直接创建一个空的Optional对象
Optional<String> op5 = Optional.empty();
基本方法介绍和使用
/**
* Optional 中常用方法介绍
* get(): 如果Optional有值则返回,否则抛出NoSuchElementException异常
* ps:get() 通常和isPresent()方法一起使用
* isPresent(): 判断是否包含值,包含值返回true,不包含返回false
* orElse(value):如果调用对象包含值,就返回值,否则返回value
* orElseGet(lambda):和orElse差不多,但是orElseGet(lambda)只有对象为空时才创建对象
*/
@Test
public void test01(){
Optional<String> op1 = Optional.of("张三");
Optional<String> op2 = Optional.empty();
if (op1.isPresent()){
System.out.println("当前值为:" + op1);
}else {
System.out.println("当前对象没有值呢");
}
if (op2.isPresent()){
System.out.println("当前值为:" + op2);
}else {
System.out.println("当前对象没有值呢");
}
System.out.println(op1.orElse("刘明"));
System.out.println(op2.orElse("李四"));
String s1= op2.orElseGet(() -> "hello");
System.out.println(s1);
}
高级用法
首先可以用ifOptional
@Test
public void test01(){
Optional<String> op1 = Optional.of("张三");
Optional<String> op2 = Optional.empty();
// 如果存在值 就做什么
op1.ifPresent(s-> System.out.println("有值:" + s));
op1.ifPresent(System.out::println);
}
接下来介绍一下判断对象空值方法
获取Employee对象名字并返回大写形式
public String getName(Employee employee){
if (employee != null){
String name = employee.getName();
if (name != null){
return name.toUpperCase();
}else {
return null;
}
}else {
return null;
}
}
如果用Optional方式:
public String getName(Optional<Employee> employee) {
if (employee.isPresent()) {
String msg = employee.map(Employee::getName)
.orElse("空值");
return msg;
}
return null;
}