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 | 余斗 阝日