lambda表达式的使用

note1

lambda是java1.8后新增的功能,一定要确保jdk在1.8及以上才能使用。

stream()函数是主要用于集合,对于数组没效果。

把集合装成流,可以多次使用这个集合;不转换流,只能用一次。这就是转换成流的好处。

note2

流与集合另一个区别在于他们的遍历方式,遍历集合通常使用for-each方式,这种方式称为外部迭代,而流使用内部迭代方式:

// 外部迭代
List<String> list = Arrays.asList("A", "B", "C", "D");
for (String str : list) {
    System.out.println(str);
}

// 内部迭代
list.stream().forEach(a -> {
      System.out.println(a);
});

note3

只能遍历一次,遍历结束后,这个流就被关闭掉了。如果要重新遍历,可以从数据源(集合)中重新获取一个流。如果你对一个流遍历两次,就会抛出java.lang.IllegalStateException异常 

List<String> list = Arrays.asList("A", "B", "C", "D");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);

// 这里会抛出java.lang.IllegalStateException异常,因为流已经被关闭
stream.forEach(System.out::println); 

note4

转换操作都是lazy的,多个转换操作只会在聚合操作的时候融合起来,一次循环完成。

换言之:Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在聚合操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。

转换操作:可以在上一个操作结果上继续操作(eg:filter -> sort -> map)

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

聚合操作:链式编程结束,执行结果

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

无状态:元素的处理不受之前元素的影响;

有状态:该操作只有拿到所有元素之后才能继续下去。

非短路操作:必须处理所有元素才能得到最终结果;

短路操作:遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

基本语法格式

(parameters) -> expression

(parameters) ->{ statements; }

举例:

// 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)

Tip1: 

        1、当lambda表达式的参数个数只有一个,可以省略小括号

        2、当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号

List<String> proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
//改前
List<String> lowercaseNames1 = proNames.stream().map((name) -> {
        return name.toLowerCase();
    }).collect(Collectors.toList());
//改后
List<String> lowercaseNames2 = proNames.stream().map(name -> name.toLowerCase())
        .collect(Collectors.toList());

 Tip2

        lambda表达式访问外部变量有一个非常重要的限制:变量不可变(只是引用不可变,而不是真正的不可变)。

List<String> proNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});

List<String> lowercaseNames1 = proNames.stream().map((name) -> {
        proNames.add("hello");   //报错,不可以修改     如果是String类型,编译不通过
        return name.toLowerCase();
}).collect(Collectors.toList());

 通过函数创建流

 java.util.stream.Stream中有两个静态方法用于从函数生成流,他们分别是Stream.generate()Stream.iterate()

一般来说,iterate()用于生成一系列值,比如生成以当前时间开始之后的10天的日期:

Stream.iterate(LocalDate.now(), date -> date.plusDays(1))
        .limit(10).forEach(System.out::println);

generate()方法用于生成一些随机数,比如生成10个UUID:

Stream.generate(() -> UUID.randomUUID().toString())
        .limit(10).forEach(System.out::println);

Stream接口中的常见方法

filter():对流的元素过滤

map():将流的元素映射成另一个类型

distinct():去除流中重复的元素

sorted():对流的元素排序

forEach():对流中的每个元素执行某个操作

peek():与forEach()方法效果类似,但该方法会返回一个新的流,而forEach()无返回

limit(N):截取流中前面几个元素

skip(N):跳过流中前面几个元素

toArray():将流转换为数组

reduce():对流中的元素归约操作,将每个元素合起来形成一个新的值

collect():对流的汇总操作,比如输出成List集合

anyMatch():匹配流中的元素,类似的操作还有allMatch()noneMatch()方法

findFirst():查找第一个元素,类似的还有findAny()方法

max():求最大值

min():求最小值

count():求总数

List<Integer> nums =Arrays.asList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("sum is:"+nums.stream()
      .filter(num -> num != null)    // 流中:1,1,2,3,4,5,6,7,8,9,10
      .distinct()                    // 流中:1,2,3,4,5,6,7,8,9,10
      .mapToInt(num -> num * 2)      // 流中:2,4,6,8,10,12,14,16,18,20
      .peek(System.out::println)     // 当元素被消费时,打印自身:2,4,6,8,10,12
      .skip(2)         // 流中:6,8,10,12,14,16,18,20
      .limit(4)        // 流中:6,8,10,12
      .sum());         // 总计:36 = 6+8+10+12


结果:
2
4
6
8
10
12
sum is:36

常用的功能语法

 1.循环遍历

int[] arr = [0,1,2,3,4,5];  
List<Integer> lsit =  Arrays.asList(arr);  
  
// for循环遍历  
for (Integer i : list) {  
     System.out.print(i+ ", ");  
}  
  
// lambda 表达式遍历
players.forEach((i) -> System.out.print(i + "; "));  
// 或
list.stream().forEach(a -> {
      System.out.println(a);
});
   
// 在 Java 8 中使用双冒号操作符遍历
// (Class or instance :: method)
list.forEach(System.out::println);
// 或
list.forEach(a -> System.out.println(a));

2.sort

String[] players = {"Rafael Nadal", "Novak Djokovic", "Stanislas Wawrinka", 
    "David Ferrer","Roger Federer", "Andy Murray",  "Tomas Berdych", 
    "Juan Martin Del Potro", "Richard Gasquet", "John Isner"};  
   
// 1.1 使用匿名内部类根据 name 排序 players  
Arrays.sort(players, new Comparator<String>() {  
    @Override  
    public int compare(String s1, String s2) {  
        return (s1.compareTo(s2));  
    }  
});

// 1.2 使用 lambda expression 排序 players  
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));  
Arrays.sort(players, sortByName);  
  
// 1.3 也可以采用如下形式:  
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2))); 

//1.4
studentList.stream().sorted(Comparator.comparing(StudentInfo::getAge)).collect(Collectors.toList())

3.filter(lambda+stream)

eg1:

// 定义 filters  
Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);  
Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));  
  
System.out.println("下面是年龄大于 24岁的PHP程序员:");  
phpProgrammers.stream()  
          .filter(ageFilter)  
          .forEach(p -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));  
  
// 重用filters  
System.out.println("年龄大于 24岁的女性 Java programmers:");  
javaProgrammers.stream()  
          .filter(ageFilter)  
          .filter(genderFilter)  
          .forEach(p -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

eg2:

User user = new User(1L, "by", 18, "音乐");
User user1 = new User(2L, "gz", 26, "音乐");
User user2 = new User(3L, "aby", 19, "旅行");
List<User> userList1 = Lists.newArrayList(user, user1, user2);
List<User> userList2 = Lists.newArrayList(user, user1);

//userList - newList
List<User> resultList = createList().stream()
        .filter(o->!newList.contains(o)).collect(Collectors.toList());

4.限制结果集个数:limit(lambda+stream)

System.out.println("最前面的3个 Java programmers:");  
javaProgrammers.stream()  
          .limit(3)  
          .forEach(p -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));  

Random+limit

//该random函数若是不传递参数,那么就采用当前时间的毫秒数当做种子数;
//若是传递了参数,就用传递的数字作为种子数了,点几次,结果都基本一样
//   因为传入的种子数限制了函数的选择性
Random random = new Random();

//受到limit限制,只会随机显示3组数字,因为没有传递参数,那么每次点击都会不一样
random.ints().limit(3).forEach(a -> {
      System.out.println(a);
});
System.out.println("-----------");

//下面的都一样
Random random2 = new Random(3);//传递了种子数
random2.ints().limit(3).forEach(a -> {
      System.out.println(a);
});

5.排序(lambda+stream)

1)List排序:正序或逆序

自然序排序一个list
list.stream().sorted() 
 
自然序逆序元素
list.stream().sorted(Comparator.reverseOrder()) 

使用Comparator 正序
list.stream().sorted(Comparator.comparing(Student::getAge)) 
list.sort(Comparator.comparing(Student::getAge)) 
 
使用Comparator 逆序
list.stream().sorted(Comparator.comparing(Student::getAge).reversed()) 
list.sort(Comparator.comparing(Student::getAge)).reversed()) 

2)根据名字和薪水排序Java程序员,放到一个list中,然后显示列表

System.out.println("根据 name 排序,并显示前5个 Java programmers:");  
List<Person> sortedJavaProgrammers = javaProgrammers  
          .stream()  
          .sorted((p1, p2) -> (p1.getFirstName().compareTo(p2.getFirstName())))  
          .limit(5)  
          .collect(toList());  
  
sortedJavaProgrammers.forEach(p1 -> System.out.printf("%s %s; %n", p1.getFirstName(), p1.getLastName()));  
   
System.out.println("根据 salary 排序 Java programmers:");  
sortedJavaProgrammers = javaProgrammers  
          .stream()  
          .sorted( (p1, p2) -> (p1.getSalary() - p2.getSalary()) )  
          .collect( toList() );  
  
sortedJavaProgrammers.forEach((p1) -> System.out.printf("%s %s; %n", p1.getFirstName(), p1.getLastName()));

6.最大值/最小值(min和max,更快):

System.out.println("工资最低的 Java programmer:");  
Person pers = javaProgrammers  
          .stream()  
          .min((p1, p2) -> (p1.getSalary() - p2.getSalary()))  
          .get();  
  
System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary()) 

 7.输出 map ,使用 collect 方法来收集结果集

1)没有父子属性(没有嵌套循环):tomap

eg1:

Dict dict=new Dict();
Map<String, Integer> map = dict.stream()
                .collect(Collectors.toMap(d->d.getName(),d->d.getCode()));


@Data
public static class Dict {
        private Integer code;
        private String name;
        
        public Dict (Integer code, String name) {
            this.code = code;
            this.name = name;
        }
}

eg2:

User user = new User(1L, "by", 18, "音乐");
User user1 = new User(2L, "gz", 26, "音乐");
User user2 = new User(3L, "aby", 19, "旅行");
List<User> userList = Lists.newArrayList(user, user1, user2);


//key = userName , value = love
Map<String, String> ssMap = userList.stream()
        .collect(Collectors.toMap(o -> o.getUserName(), o -> o.getLove()));

//key = id , value = User
Map<Long, User> luMap = userList.stream()
        .collect(Collectors.toMap(o -> o.getId(), o -> o));

//key = love + userName , value = User
Map<String,User> map = new HashMap<>(16);
userList.forEach(o->{
        map.put(o.getLove()+o.getUserName(),o);
});

2)有父子属性(有嵌套循环):flatmap

Map<String, Integer> map=dict.stream().flatMap(d->d.getChildren().stream())
                .collect(Collectors.toMap(d->d.getName(),d->d.getCode()));

@Data
public static class Dict{
        private Integer code;
        private String name;
        private List<Dict> children=new ArrayList<>();
      
        public Dict(Integer code, String name) {
            this.code = code;
            this.name = name;
        }
}

8.去重

//用数组来转换集合
List<Integer> list = Arrays.asList(9,3,3);
//distinct()函数,是去重复函数
list = list.stream().distinct().collect(Collectors.toList());
//打印输出list
list.forEach(a -> {
      System.out.println(a);
});

9.List<>转List<map<,>>

List<Map<String,Object>> personToMap = peopleList.stream().map((p) -> {
    Map<String, Object> map = new HashMap<>();
    map.put("is", p.getId());
    map.put("age", p.getAge());
    return map;
}).collect(Collectors.toList());

10.stream实现双重for循环

11.String[] 转int[]  -->  String数组转int数组

int[] ids= Arrays.stream(Strarr).mapToInt(o -> Integer.parseInt(o)).toArray();
或
int[] array = Arrays.asList(strings).stream().mapToInt(Integer::parseInt).toArray();

12.Map和String的转换

假设String="{1-2,2-2}";

map转String

String s = map.keySet().stream()
      .map(key -> key + "-" + map.get(key))
      .collect(Collectors.joining(", ", "{", "}"));

 如果不需要大括号{}

String s = map1.keySet().stream()
                .map(key -> key + "-" + map1.get(key))
                .collect(Collectors.joining(", "));

String转map

Map<String, String> map = Arrays.stream(s.split(","))
      .map(o-> o.split("-"))
      .collect(Collectors.toMap(o-> o[0], o-> o[1]));

13.forEach条件下,如果不满足条件,希望跳到下一个循环用return

list.stream().forEach(o-> {
      if(!o.getId().equals(id)){
            return;
      }
      System.out.println(id);
}

14.实现两次分组groupBy(可实现多条件分组)

//进行二次groupBy:1.按Time  2.按Level
Map<String, Map<String, List<DTO>>> Map = list.stream()
       .collect(Collectors.groupingBy(o -> o.getTime(), Collectors.groupingBy(p -> 
       p.getLevel())));

15.遍历map

//假设已有HashMap map数据
map.entrySet().forEach(o -> {
            String realName = o.getKey();
            String email = o.getValue();
            System.out.println(realName+""+email);
        });

或者

users.forEach((realName, email) -> {
            System.out.println(realName+""+email);
        });

16.map按key排序

正序:

Map<String, List<Map.Entry<String, JSONArray>>> collect = collect.entrySet()
      .stream()
      .sorted(Map.Entry.comparingByKey())
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

逆序:

Map<String, List<Map.Entry<String, JSONArray>>> collect = collect.entrySet()
      .stream()
      .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

17.map按value排序

正序:

Map<LocalDate, BigDecimal> map = map.entrySet()
        .stream()
        .sorted(Map.Entry.comparingByValue())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

18.List逆序

list = finalList.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

19.map按指定的key顺序排序

List<String> rules = Arrays.asList("d", "c", "a");
HashMap<String, String> map = new HashMap<>();
map.put("a", "1");
map.put("d", "2");
map.put("c", "3");
LinkedHashMap<String, String> result= map.entrySet().stream()
    .sorted(Comparator.comparingInt(k -> rules.indexOf(k.getKey())))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
result.entrySet().stream().forEach(c-> System.out.println(c.getKey()+" "+c.getValue()));

结果:
d 2
c 3
a 1

Collectors.toMap()有三个签名:
1.Collects.toMap(Map.Entry::getKey, Map.Entry::getValue);
参数一是转化后的key,参数二是转化后的value,此方法下,发生碰撞会直接丢异常
2.Collects.toMap(Map.Entry::getKey, Map.Entry::getValue,(oldValue, newValue) -> oldValue);
参数一二同上,参数三是发生碰撞时保持旧值
3.Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)
参数一二三同上,最后一个是你想生成的Map结构,这里采用的是LinkedHashMap.如果想实现排序后仍然保持Map结构,那么LinkedHashMap是不错的选择。

20.计算List数据总和

eg:Student(Integer socre, String stuName, String subject)

Map<String, Integer> stuMap = new HashMap<>();
studentList.forEach(stu -> stuMap.merge(stu.getStuName(),stu.getScore(),Integer::sum));

21.两属性相乘后累加

 假设求商品总价格

int sum += data.getValue().stream()
        .filter(o -> o.getPrice() != null)
        .filter(o -> o.getNum != null)
        .mapToLong(o -> (o.getPrice() * o.getNum))
        .reduce(0, Long::sum);

文章参考:

Java中Lambda表达式的使用 - Franson - 博客园

Java8中Stream详细用法大全 - 森林木马 - 博客园

Java 8新特性(二):Stream API | 余斗 阝日

Java Stram实现Map和字符串之间互相转换| Baeldung

使用Stream对HashMap进行排序 - 简书

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值