JAVA笔记 | JAVA8简单又详细的快速入门笔记

目录

目录

背景

一个简单的lambda表达式例子 

函数式接口

特点

四大函数式接口

Function函数型接口

Predicate(判断型接口)

Consumer消费型接口

Supplier提供型接口

谓词逻辑

基础操作总结

forEach遍历

map转换

flatMap扁平化流

查找与匹配 

anyMatch()

allMatch()

noneMatch()

findFirst()

isPresent()

ifPresent()

orElse()

元素连接joining

规约reduce

Optional及相关操作

理解

empty()创建空的Optional对象

of()创建非空的Optional对象

ofNullable()创建允许为null的Optional对象

isPresent()判断Optional对象是否存在,存在返回true,不存在返回false

ifPresent() 如果存在后做什么

ifPresentOrElse 如果存在做什么,否则做什么

orElse()如果对象为null做什么

orElseGet() 对象为null根据表达式做什么

get()获取对象值

filter() 过滤

map()转换值

流操作过程的理解

源操作

中间操作

终端操作

收集器Collectors

分组收集groupingBy

根据某一个字段值分组,并统计

多次分组嵌套

自定义key值得分组

键为日期字符串顺序排序

分组收集partitioningBy

聚合操作/统计

count() 计数

sum()求和

 average()求平均值

max()/min() 最大值和最小值

IntSummaryStatistics的使用

有状态操作与无状态操作

limit(n)

skip(n)

distinct()

去重拓展

sorted()

排序拓展

并行流


背景

个人复习总结笔记,可能有点乱吼,后续学习继续完善,白话+自己的理解

一个简单的lambda表达式例子 

public interface Demo {
    void test(String a);
}

一个功能接口作为参数进行传递的简化(原始->lambda)

public class TestMethod {
    public static void main(String[] args) {
        //原始:一个功能接口的实现
        Demo demo = new Demo() {
            @Override
            public void test(String a) {
                System.out.println(a);
            }
        };
        //简化
        Demo demo1 = (String a) ->{
            System.out.println(a);
        };
        //进一步简化
        Demo demo2 = a -> System.out.println(a);
        //TestMethod testMethod = new TestMethod();
        //testMethod.print("test",a -> System.out.println(a));

        TestMethod testMethod = new TestMethod();
        testMethod.print("test",demo);
    }

    //通过传入不同的Demo接口的实现完成不同的功能
    public void print(String a,Demo demo){
        demo.test(a);
    }
}

集合List,数组,文件流能用Stream

集合list.stream

数组Stream.of(array)

文件流

此处接口中如果存在多个方法,则不能使用lambda表达式,因为不知道会调用哪个方法,故考虑

函数式接口

特点

只有一个抽象方法(没有方法的实现)的接口就为函数式接口;

函数式接口可以通过 lambda 表达式、方法引用、构造方法引用来创建实例;

接口带注解@FunctionalInterface则强制为函数式接口;

可定义静态非抽象方法,可定义非抽象(带有函数体)默认方法default(实现类可以不用实现);

可以不带@FunctionalInterface,允许存在java.lang.Object中的public方法,如equals;

四大函数式接口

Function函数型接口

输入值,返回处理后的值,T输入类型,R返回类型

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
    ......
}

举例

Function<String,Integer> function = s -> s.length();
//输出 4
System.out.println(function.apply("test"));

Predicate(判断型接口)

输入一个值进行判断,返回布尔类型

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
......
}

举例

Predicate<String> predicate = s -> s.length > 10;
//输出false
System.out.println(predicate.test("test"));  

Consumer消费型接口

传入值,无返回值

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
......
}

举例

Consumer<String> consumer = s -> System.out.println(s);
//输出test
consumer.accept("test");

Supplier提供型接口

无传入值,但有输出值

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

举例

Supplier<String> supplier = ()->{return "test";};
//输出test
System.out.println(supplier.get());

谓词逻辑

谓语,用来修饰限定主语

如Predicate 用于校验的谓词逻辑,当一个判断逻辑多出地方用到,就可以提取出来

简单理解为一个lambda表达式抽取出来

多个谓词逻辑共同成立 用.and()连接 表示共同成立,其他连接词根据业务逻辑来定

如 .or()或 .negate() 取相反的类似非

        List<User> users = Arrays.asList(new User(12,"M"),new User(null,"M"));
//        List<User> user2 = users.stream().filter(e -> {
//            if(e.getAge() == null){
//                e.setAge(100);
//                return true;
//            }
//            return e.getAge() != null && e.getAge() > 10;
//        }).collect(Collectors.toList());
        List<User> user2 = users.stream().filter(predicateTest()).collect(Collectors.toList());
        user2.stream().forEach(System.out::print);
    }


    static Predicate<User> predicateTest() {
        return e -> {
            if(e.getAge() == null){
                e.setAge(100);
                return true;
            }
            return e.getAge() != null && e.getAge() > 10;
        };
    }

谓词逻辑可无限套娃

public class Test {
    public static void main(String[] args) {


        List<User> users = Arrays.asList(new User(12,"M"),new User(11,"W"));
    //        List<User> user2 = users.stream().filter(e -> {
//            if(e.getAge() == null){
//                e.setAge(100);
//                return true;
//            }
//            return e.getAge() != null && e.getAge() > 10;
//        }).collect(Collectors.toList());
    List<User> user2 = users.stream().filter(predicateTestAll()).collect(Collectors.toList());
        user2.stream().forEach(System.out::print);
}


    static Predicate<User> predicateTest() {
        return e -> {
            if(e.getAge() == null){
                e.setAge(100);
                return true;
            }
            return e.getAge() != null && e.getAge() > 10;
        };
    }

    static Predicate<User> predicateTest2() {
        return e -> e.getSex().equals("M") ;
    }
    
    static Predicate<User> predicateTestAll() {
        return predicateTest().and(predicateTest2());
    }

}

基础操作总结

forEach遍历

举例:输出流中的元素

list.stream().forEach(System.out::println);

map转换

1.map 将流中的元素从一种格式转换为另外一种格式(类型)如 Integer  ->String 

stream.map(e -> Integer..parseInt(e))

2.mapToInt 将元素转为整型

Stream.of("a","b").mapToInt(String::length)

3.map与foreach有时候可以完成一样的功能,但是map操作如果是复杂操作的话(值得是没用方法引用),需要用return返回最终的结果元素,foreach不用。

//map
list.stream.map(e -> {
    e.setAge(1);
    return e;
}).collect(Collectors.toList());
//foreach
list.foreach(e -> {
    e.setAge(1);
})

flatMap扁平化流

flatMap处理多个集合且合并,或者一个元素处理后变成一个数组,最终结果是集合的集合(List<List<String>>),数组中有数组,管道中有管道等多维的情况,此时要合成一个简单一维的字符串数组或者直接输出,此时单单map就有点复杂

List<String> sss = Arrays.asList("abb","sss");
        List<String[]> ss2 = sss.stream().map(e -> e.split("")).collect(Collectors.toList());
        ss2.stream().forEach(System.out::print);

此时打印出两个数组,但不是需要的值

[Ljava.lang.String;@6267c3bb[Ljava.lang.String;@533ddba

如果不用java8输出,那么需要两个for循环,采用java8 map的话就不能嵌套循环输出,所以此时考虑用flatMap扁平化(理解,把不同集合的元素 拍进同一个流中)

进一步简单理解为:flatMap 将集合中的集合元素转成多个流,再进行操作,然后合并这些流,合成最终流。

List<String> ssss = Arrays.asList("abb","sss");
        ssss.stream().flatMap(e -> Arrays.stream(e.split("")))
                .forEach(System.out::print);

//List<String> s = ssss.stream().flatMap(e -> //Arrays.stream(e.split(""))).collect(Collectors.toList());

查找与匹配 

匹配

anyMatch()

是否存在至少一个符合要求的元素 存在返回true,不存在返回false

boolean result = list.stream().anyMatch(e -> e.getAge > 10);

allMatch()

是否所有元素都符合条件 是true 反之false

noneMatch()

是否不存在符合条件的元素 

查找

findFirst()

查找第一个元素

Optional<User> result = list.stream().filter(e -> e.getAge > 10).findFirst();
User u = result.get();

isPresent()

是否存在

ifPresent()

如果存在做什么

orElse()

如果不存在做什么

元素连接joining

流中元素用指定符号连接或直接连接

//所有元素用逗号连接成字符串
String s = list.stream().collect(Collectors.joining(","));

规约reduce

目的:将一个集合转换为一个对象(如Integer,String等)

举例:

将集合中的元素累加,得出最终值

*reduce(初始值,累计操作器,合并器)

reduce(初始值,lambda表达式) 其中lambda表达式(a,b) -> {} 其中a表示当前结果,b为元素

累加

Integer sum = list.stream().reduce(0,(temp , e) -> temp + e);
//或
Integer sum2 = list.stream().reduce(0,Integer::sum);

 字符串拼接

String str = list.stream().reduce("",String::concat);

当设计到parallelStream()并行流时,reduce将有第三个参数,为合并器,将分组的结果进行合并

如此处将元素 1 2 3 4 分为两组1+2,3+4进行计算,最后通过合并器将两组结果合并为最终结果

Integer sum = list.parallelStream().reduce(0,Integer::sum,Integer::sum);

Optional及相关操作

理解

一个包装类,可以为null的容器对象,将结果进行包装,防止空指针异常。如果Optional包装后的对象,值存在调用isPresent()方法返回true,调用get()方法返回该对象, 如果不存在抛异常

empty()创建空的Optional对象

Optional<String> o = Optional.empty();

of()创建非空的Optional对象

此时of中的参数不能为null,否者也会抛出异常

Optional<String> o = Optional.of("test");

ofNullable()创建允许为null的Optional对象

Optional<String> o = Optional.ofNullable(null);

isPresent()判断Optional对象是否存在,存在返回true,不存在返回false

Optional<String>  o = Optional.of("test");
Boolean s = o.isPresent(); 

ifPresent() 如果存在后做什么

Optional<String> o = Optional.of("test");
o.ifPresent(s -> System.out.println(s.length()));

ifPresentOrElse 如果存在做什么,否则做什么

Optional<String> o = Optional.of("test");
o.ifPresentOrElse(s -> System.out.println(str.length()), 
    () -> System.out.println("对象为空"));

orElse()如果对象为null做什么

String s = Optional.ofNullable(null).orElse("test");

orElseGet() 对象为null根据表达式做什么

String s = Optional.ofNullable(null)
    .orElseGet(()->"test");

get()获取对象值

存在问题:当对象值为null,则会抛异常,所以一般用orElseGet()来获取

 Optional<String> o = Optional.ofNullable("test");
 System.out.println(o.get());

filter() 过滤

符合条件返回对象,不符合返回空Optional对象

Optional<String> o = Optional.ofNullable("test");        
Boolean result = o.filter(e -> e.length() > 1).isPresent();

map()转换值

生成新的对象,原值不变

Optional<String> o1 = Optional.of("test");     
Optional<Integer> o2 = o1.map(String::length); 

流操作过程的理解

流操作的过程可以分三部分(通俗比喻:冰川水入海流)

源操作

河流源头,雪山、冰川、湖泊等  -》 数组,集合,行文本文件

中间操作

河流河床经过的一些地方及人们对河流的影响及操作  -》(无状态操作)filter,map,flatmap,(有状态操作)limit,distinct,skip,sorted等

终端操作

入海 -》.collect(Collectors.asList());

.collect(Collectors.toSet());

收集器Collectors

//收集为集合
.collect(Collectors.asList());
//收集为set
.collect(Collectors.toSet());
//收集成通用集合
.collect(Collectors.toCollection(LinkedList::new));
.collect(Collectors.toCollection(LinkedHashSet::new));
.collect(Collectors.toCollection(PriorityQueue::new));
//收集为数组
.toArray(String[]::new);
//收集为map 指定键值
.collect(Collectors.toMap(User::getId,User::getName));
//指定键,值为对象本身
.collect(Collectors.toMap(User::getId,User->User));
.collect(Collectors.toMap(User::getId, Function.identity()));//效果同上
//map中重复key的解决方法
//第二个key覆盖第一个key 或者 事先将充当key值得元素去重等
.collect(Collectors.toMap(User::getId, Function.identity(),(key1,key2)->key2));

分组收集groupingBy

根据某一个字段值分组,并统计

//根据年龄分组,统计各个年龄对应多少人
Map<String,Long> m = list.stream() 
    .collect(Collectors.groupingBy(User::getAge,Collectors.counting()));

多次分组嵌套

//先按班级分组,再按性别分组,统计每班男女各多少人
Map<String,Map<String,Long>> m = list.stream()
    .collect(Collectors.groupingBy(User::getClass,                      
        Collectors.groupingBy(User::getSex,Collectors.counting())));

自定义key值得分组

//统计已成年,未成年分别多少人,键为已成年,未成年
Map<String,Long> m =                
    games.stream().collect(Collectors.groupingBy(e -> {
                                if(e.getAge() > 18 ){
                                    return "已成年";
                                }else {
                                    return "未成年";
                                },Collectors.counting()));

键为日期字符串顺序排序

对键进行排序最后有序存入map中

//数据源,键为日期字符串
Map<String,Long> map = Maps.newHashMap();
map.put("1-25",4L);
map.put("1-21",3L);
map.put("1-22",4L);

//时间排序且结果有序
Map<String,Long> result = Maps.newLinkedHashMap();
map.entrySet().stream().sorted(Map.Entry.comparingByKey())
    .forEachOrdered(e -> result.put(e.getKey(),e.getValue()));

//打印结果
result.entrySet().forEach(System.out::println);

分组收集partitioningBy

通过条件表达式,将结果分为键为true,false两部分的map

//将年龄大于18岁的学生作为键是true的值,其他不符合条件的元素为键是false的值
Map<Boolean, List<User>> map = 
    list.stream().collect(Collectors.partitioningBy(e -> {
           return e.getAge() > 18;
    }));

聚合操作/统计

count() 计数

//通常与过滤一起使用
long count = list.stream().count();

sum()求和

int sum = list.stream().sum();

 average()求平均值

OptionalDouble average = list.stream().average();

max()/min() 最大值和最小值

int max = list.stream.max();

IntSummaryStatistics的使用

List<Integer> nums = Arrays.asList(1,2,3,4,5,6);
IntSummaryStatistics statistics = nums.stream().mapToInt(x -> x).summaryStatistics();
System.out.println(statistics.getMax());

getMax()最大值

getMin()最小值

getSum()和

getAverage()平均数

有状态操作与无状态操作

对于中间操作 有状态无状态的理解

无状态操作是对本身进行操作比如自身转换,过滤,与别人无关

有状态则需要参照前后元素位置改变等,如对比后排序,跳过,对比去重

limit(n)

取前n个元素

skip(n)

跳过前n个取后面元素

distinct()

去重 -》衍生自定义去重

去重拓展

通过对象中某个字段来去重,通过filter + map去重来实现

List<User> result = list.stream()
    //通过map对数据进行去重,map.put方法如果键对应有值存在,则返回旧的值,塞入新的值,否则返回null
    .filter(e -> map.put(e.getName(), e) == null)
    .collect(Collectors.toList());

//提取出来
private static Predicate<User> userDistinct() {
    final Map<String, User> map = new ConcurrentHashMap<>();
    return e -> map.put(e.getName(), e) == null;
}

//或者使用Set,并发场景下考虑使用
private static Predicate<User> userDistinct2() {
    final Set<String> set = new HashSet<>();
    return e-> set.add(e.getName());
}

sorted()

自然排序 -》衍生自定义排序

排序拓展

集合的常规排序

以前集合通过Collections.sort()来进行排序,通过传递排序规则来进行自定义排序

如list.sort(String.CASE_INSENSITIVE_ORDER) //不考虑大小写按字母表顺序排序

倒序排序:list.sort(Comparator.comparing(User::getAge)).reversed());

多个排序规则:list.sort(Comparator.comparing(User::getAge).thenComparingInt(User::getId));

java8 sorted自定义排序

前提:流中元素必须实现Comparable接口

(1) 自然升序 一般用于List<String>,List<Integer>

list.stream().sorted().collect(Collectors.toList());

(2)根据条件升序 List<User>,根据实体中某个字段

list.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());

(3)自然降序

ist.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

(4)根据条件降序

list.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());

(5)多个字段排序

list.sorted(Comparator.comparing(User::getAge).thenComparing(User::getId)).collect(Collectors.toList());

(6)含空值排序

如果排序条件含有null的,可以排到最后,不处理的话会报空指针异常

list.stream().sorted(Comparator.comparing(User::getAge,Comparator.nullsLast(String::compareTo))).collect(Collectors.toList());

并行流

1.流的串行(sequential()默认)与并行(parallel())

之前的操作都为串行,加上.parallel()变为并行流

串行流:一个个处理,保证输入与输出顺序对应

并行流:并行处理,速度更快,但顺序改变(注意有状态的操作会收到影响,因为分组并行,与最终整体结果的处理会不一致)

不适合用并行流的情况:LinkedList链表(区别ArrayList有下标方便并行时分组),阻塞队列,IO流等。适合:ArrayList,HashMap,数组

运行效率对比:

不能简单对比,并行流与cpu资源有关,且数据量越大,Stream流的执行效率越高,所以一般情况可能运行的速度 并行流大于for循环大于可能等于串行流

parallelStream() 与stream.parallel() 相同

2.使用并行流时候forEach()与forEachOrdered()的区别

forEach() 由于并行流分组处理,故输出不保证顺序

forEachOrdered() 按照元素输入的顺序进行输出

(后续完善更新中...)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值