Stream相关API学习使用记录--(一)
一、前言
在以前的工作中,业务中很多时候是需要循环来获取某一个列表中的一些数据,比如获取某一个值做业务逻辑的判断,或者获取其中一部分的数据,如使用状态的标志位等等,这个时候可能很多时候都是使用简单的for循环、Foreach循环或者迭代器等方式,很多时候为了处理业务都是需要循环嵌套循环的。但是在Java8中为集合的相关处理提供了一个更强大的工具,那么就是Stream,在浏览代码的时候看见了其他人的书写,感觉很惊奇(此时自己也很无语,Java8出来这么久了,自己好像好没有意识到它到底添加了那些新的内容,有什么新的特性,简化了那些操作等等),所以自己就对这个东西充满了好奇。
二、 Stream的基础简介
2.1、stream是什么
它是Java8 API添加的一个新的抽象流,它把要处理的元素看做一种流,流在管道中传输,并且可以在管道的节点上进行处理,例如排序,筛选和聚合等操作,可以让人以一种声明的方式来处理数据。
元素流在管道中经过中间操作(intermediate operation)处理,最后由最终操作(terminal operation)得到前面的处理结果。
+------------------------+ +---------+ +-----------+ +--------+ +-------+
| stream of elements +--------> |filter +-----> |sorted +-----> |map +----> |collect|
+-----------------------+ +---------+ +-----------+ +--------+ +-------+
2.2、特性
stream是一个来自数据源的元素队列并支持聚合操作:
(1)元素是特定的元素,形成一个队列,但是stream不会存储元素,而是按需计算
(2)数据源可以是集合、数组、I/Ochannel等
(3)集合操作类似链式的SQL,如filter、map、reduce、find、match、sortde等
2.3、流生成方式
2.3.1、串行流
使用stream方式,为集合创建串行流
2.3.2、并行流
使用parallelStream 为集合创建并行流
三、管道节点
3.1、ForEach
Stream提供了新的方法来迭代流中的每一个数据
public class StreamDemoTest { private final static Integer MAX_CIRCLE = 100; public static void main(String[] args) { List<Integer> strList = new ArrayList<>(); Map<Integer, String> integerMap = new HashMap<>(); for (int i = 0; i < MAX_CIRCLE; i++) { strList.add(i); integerMap.put(i, i+"e"); } strList.stream().forEach(s -> System.out.println(s)); } }
3.2、map
map方法用于映射每个元素到对应的结果
public class StreamDemoTest { private final static Integer MAX_CIRCLE = 100; public static void main(String[] args) { List<Integer> strList = new ArrayList<>(); Map<Integer, String> integerMap = new HashMap<>(); for (int i = 0; i < MAX_CIRCLE; i++) { strList.add(i); integerMap.put(i, i+"e"); } //strList.stream().forEach(s -> System.out.println(s)); List<Integer> integerList = strList.stream().map(i -> i * i).collect(Collectors.toList()); integerList.stream().forEach(s -> System.out.println(s)); } }
3.3、filter
filter用于通过设置的条件过滤出元素
List<String> stringList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 long count = stringList.stream().filter(string ->string!="abc").count(); //获取不是abc和空的字符串 List<String> list = stringList.stream().filter(str -> str != "abc"&&str!="").collect(Collectors.toList());
3.4、limit
用于获取指定数量的流
List<String> stringList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 long count = stringList.stream().filter(string ->string!="abc").count(); //限定获取过滤条件后的数量 List<String> list = stringList.stream().filter(str -> str != "abc"&&str!="").limit(3).collect(Collectors.toList()); list.stream().forEach(s-> System.out.println(s));
limit的位置可以在filter前面或者后面,只是表示的含义不一样而已。
3.4、sorted
sorted用于对流进行排序
public static void main(String[] args) { List<String> strList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); //自然排序 System.out.println("自然排序"); strList.stream().sorted().forEach(s -> System.out.println(s)); System.out.println("倒序排序"); strList.stream().sorted(Comparator.reverseOrder()).forEach(s -> System.out.println(s)); }
输出结果已经按照要求进行了排序,默认的是升序排列了,如果想简单的倒序排序,可以使用 Stream<T> sorted(Comparator<? super T> comparator)方法,里面采用Comparator.reverseOrder()进行倒序排序。
如果是自定义的其他的类型,怎么进行排序那?
下面是自定义的一个用户信息的简单实体
class User { /** * 姓名 **/ private String userName; /** * 英文名称 **/ private String englishName; /** * 年龄 **/ private int age; /** * 住址 **/ private String address; /** * 电话 **/ private String tel; public User(String userName, String englishName, int age) { this.userName = userName; this.englishName = englishName; this.age = age; } public User(String userName, String englishName, int age, String address, String tel) { this.userName = userName; this.englishName = englishName; this.age = age; this.address = address; this.tel = tel; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getEnglishName() { return englishName; } public void setEnglishName(String englishName) { this.englishName = englishName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } @Override public String toString() { return "User{" + "userName='" + userName + '\'' + ", englishName='" + englishName + '\'' + ", age=" + age + ", address='" + address + '\'' + ", tel='" + tel + '\'' + '}'; } }
public static void main(String[] args) { /* List<String> strList = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); //自然排序 System.out.println("自然排序"); strList.stream().sorted().forEach(s -> System.out.println(s)); System.out.println("倒序排序"); strList.stream().sorted(Comparator.reverseOrder()).forEach(s -> System.out.println(s));*/ List<User> userList = new ArrayList<>(); User user = new User("Angle", "B", 10); userList.add(user); user = new User("Heni", "G", 39); userList.add(user); user = new User("Eson", "F", 6); userList.add(user); user = new User("Cily", "D", 2); userList.add(user); user = new User("Cily", "G", 6); userList.add(user); user = new User("Eson", "G", 6); userList.add(user); System.out.println("正常输出"); userList.stream().forEach(s-> System.out.println(s.toString())); System.out.println("按照名称默认升序排列"); userList.stream().sorted(Comparator.comparing(User::getUserName)).forEach(s -> System.out.println(s)); System.out.println("按照名称进行倒序排列"); userList.stream().sorted(Comparator.comparing(User::getUserName).reversed()).forEach(s -> System.out.println(s)); System.out.println("多元素排列"); userList.stream().sorted(Comparator.comparing(User::getUserName).thenComparing(User::getAge).reversed()).forEach(s -> System.out.println(s)); System.out.println("转换为map"); userList.stream().collect(Collectors.toMap(User::getUserName,User::getAge,(newValue,oldValue)->newValue)).entrySet().stream().forEach(e-> { System.out.println(e.getKey()+"====="+e.getValue()); }); System.out.println("过滤以后的"); userList.stream().filter(e -> e.getUserName().equals("Eson")).sorted(Comparator.comparing(User::getAge).reversed()) .forEach(e -> System.out.println(e.getUserName() + "======" + e.getAge())); System.out.println("如果是重复的key,值转为list"); Map<String, List<String>> map = userList.stream().collect(Collectors.toMap(User::getUserName,p->{ List<String> ageList = new ArrayList<>(); ageList.add(p.getAge() + ""); return ageList; },(List<String> valueNew,List<String> valueOld)->{ valueNew.addAll(valueOld); return valueNew; })); map.entrySet().stream().forEach(e-> { System.out.println(e.getKey()+"====="+e.getValue()); }); }
输出结果:
正常输出 User{userName='Angle', englishName='B', age=10, address='null', tel='null'} User{userName='Heni', englishName='G', age=39, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} 按照名称默认升序排列 User{userName='Angle', englishName='B', age=10, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Heni', englishName='G', age=39, address='null', tel='null'} 按照名称进行倒序排列 User{userName='Heni', englishName='G', age=39, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} User{userName='Angle', englishName='B', age=10, address='null', tel='null'} 多元素排列 User{userName='Heni', englishName='G', age=39, address='null', tel='null'} User{userName='Eson', englishName='F', age=6, address='null', tel='null'} User{userName='Cily', englishName='G', age=6, address='null', tel='null'} User{userName='Cily', englishName='D', age=2, address='null', tel='null'} User{userName='Angle', englishName='B', age=10, address='null', tel='null'}
可以看出来,整个排序是相当的简单了,不需要自己的类继承comparable,也不需要自己扩展排序comparator了。
对于map类型格式的数据。可以对键排序或者值排序
1 public static void main(String[] args) { 2 Map<String, String> map = new LinkedHashMap<String, String>(); 3 map.put("12", "ddd"); 4 map.put("23", "eee"); 5 map.put("34", "ddw"); 6 map.put("21", "ecc"); 7 8 System.out.println("根据值排序"); 9 map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue)).forEach(e -> { 10 String key = e.getKey(); 11 String value = e.getValue(); 12 System.out.println("键值"+key+"值"+value); 13 }); 14 System.out.println("根据键排序"); 15 map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(e -> { 16 //System.out.println(e.toString()); 17 String key = e.getKey(); 18 String value = e.getValue(); 19 System.out.println("键值"+key+"值"+value); 20 }); 21 }
输出的结果:
根据值排序
键值12值ddd
键值34值ddw
键值21值ecc
键值23值eee
根据键排序
键值12值ddd
键值21值ecc
键值23值eee
键值34值ddw
综合看下来,使用对应的Stream比使用平常简单的循环而言代码量少了很多。