在你还在使用java5\6的时候,java8\9已问世。当你在使用java8/9的时候,java12已问世。
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。(参考:https://www.runoob.com/java/java8-streams.html)
ps,如果在Linux中有点像grep,cat xx.txt | grep "*caishaolin*",如果再sql中有点像查询,包括条件 select * from t where t.name='caishaolin'。
1、filter使用
代码:准备了200条用户数据,查找id=150并且name='name-150'的用户
public class StreamTest1 { public static void main(String[] args) { filter1(); filter2(); } private static void filter1() { System.out.println("**************filter1****************"); List<User> userList = initData(); long start=System.currentTimeMillis(); List<User> users = userList.stream().filter(u -> u.getId() <= 160).filter(u->u.getName().equals("name-150")).collect(Collectors.toList()); System.out.println("耗时:"+(System.currentTimeMillis()-start)); System.out.println("最后结果:"+users); } private static void filter2() { System.out.println("**************filter2****************"); List<User> userList = initData(); long start=System.currentTimeMillis(); List<User> users = userList.stream().filter(u -> u.getId() <= 160 && u.getName().equals("name-150")).collect(Collectors.toList()); System.out.println("耗时:"+(System.currentTimeMillis()-start)); System.out.println("最后结果:"+users); } private static List<User> initData(){ List<User> userList= new ArrayList<>(); for(int i=0;i<200;i++){ userList.add(new User(i, "name-"+i)); } return userList; } } class User { public User(int id, String name) { this.id = id; this.name = name; } private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
运行结果:
**************filter1**************** 耗时:67 最后结果:[User{id=150, name='name-150'}] **************filter2**************** 耗时:0 最后结果:[User{id=150, name='name-150'}]
分析:
使用stream一行代码搞定。
如果不用,可能还得在for循环中判断 for(User user:userList){if((user.getId==150)&&(user.getName.equles("name-150"))},
也可能是使用sql条件查询 select * from user where id=#{id} and name=#{name},但在集合中过滤数据总比每次去查询数据库性能要好很多。
而且推荐使用filter2这种方法性能更好。
2、forEach使用
Stream 提供了新的方法 'forEach' 来迭代流中的每个数据
代码:
public class StreamTest1 { public static void main(String[] args) { forEach1(); } private static void forEach1(){ System.out.println("**************forEach1****************"); List<User> userList = initData(); userList.stream().forEach(StreamTest1::addAge); System.out.println("forEach 加1岁之后:"+userList); } private static List<User> initData() { List<User> userList = new ArrayList<>(); for (int i = 0; i < 200; i++) { userList.add(new User(i, "name-" + i,i)); } return userList; } public static User addAge(User user){ user.setAge(user.getAge()+1); return user; } } class User { public User(int id, String name,int age) { this.id = id; this.name = name; this.age=age; } private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } }
3、map使用
map 方法用于映射每个元素到对应的结果
代码:
public class StreamTest1 { public static void main(String[] args) { map1(); } private static void map1() { System.out.println("**************map1****************"); List<User> userList = initData(); userList.stream().map(u -> u.addAge(u)).collect(Collectors.toList()); System.out.println("map1 加1岁之后:" + userList); } private static List<User> initData() { List<User> userList = new ArrayList<>(); for (int i = 0; i < 200; i++) { userList.add(new User(i, "name-" + i, i)); } return userList; } } class User { public User(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public User addAge(User user) { user.setAge(user.getAge() + 1); return user; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } }
运行结果:运行结果和上面forEach是一样的
**************map1****************
forEach 加1岁之后:[User{id=0, name='name-0', age=1}, User{id=1, name='name-1', age=2}, User{id=2, name='name-2', age=3},...
4、sorted使用
sorted排序功能
代码:
public class StreamTest1 { public static void main(String[] args) { sorted1(); sorted2(); } private static void sorted1() { System.out.println("**************sorted1****************"); List<User> userList = initData(); //小到大 //userList = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList()); //大到小 userList = userList.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList()); System.out.println("sorted1 按年龄小到大排序之后:" + userList); } private static void sorted2() { System.out.println("**************sorted2****************"); List<User> userList = initData(); //小到大 //userList = userList.stream().sorted((x,y)->x.getAge()-y.getAge()).collect(Collectors.toList()); //大到小 userList = userList.stream().sorted((x,y)->y.getAge()-x.getAge()).collect(Collectors.toList()); System.out.println("sorted2 按年龄排序之后:" + userList); } //10个用户,年龄100只能的随机数 private static List<User> initData() { List<User> userList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Random random = new Random(); userList.add(new User(i, "name-" + i, random.nextInt(100))); } return userList; } } @Data class User { public User(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } private int id; private String name; private int age; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } }
5、Collectors使用
5.1、toMap
toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map
源码:
public final class Collectors { // 指定键和值的生成方式keyMapper和valueMapper public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {/*...*/} // 在上面方法的基础上增加了对键发生重复时处理方式的mergeFunction,比如上面的默认的处理方法就是抛出异常 public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) {/*...*/} // 在第二个方法的基础上再添加了结果Map的生成方法。 public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) {/*...*/} }
代码:
public class StreamTest1 { public static void main(String[] args) { collector1(); } private static void collector1() { System.out.println("**************collector1****************"); List<User> userList = initData(); Map<Integer, User> map = userList.stream().collect(Collectors.toMap(User::getId,Function.identity())); //从源码可以看到Function.identity()实际上是t -> t,现在写成 a -> a或者b -> b也可以 //Map<Integer, User> map = userList.stream().collect(Collectors.toMap(User::getId,t->t)); //Map<Integer, User> map = userList.stream().collect(Collectors.toMap(User::getId,a->a)); //Map<Integer, User> map = userList.stream().collect(Collectors.toMap(User::getId,b->b)); System.out.println("collector1 list转map之后:" + map); } //10个用户,年龄100只能的随机数 private static List<User> initData() { List<User> userList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Random random = new Random(); userList.add(new User(i, "name-" + i, random.nextInt(100))); } return userList; } } @Data class User { public User(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } private int id; private String name; private int age; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } }
运行效果:
**************collector1****************
collector1 按年龄小到大排序之后:{0=User{id=0, name='name-0', age=91}, 1=User{id=1, name='name-1', age=63}, 2=User{id=2, name='name-2', age=48}, 3=User{id=3, name='name-3', age=11}, 4=User{id=4, name='name-4', age=49}, 5=User{id=5, name='name-5', age=88}, 6=User{id=6, name='name-6', age=55}, 7=User{id=7, name='name-7', age=46}, 8=User{id=8, name='name-8', age=8}, 9=User{id=9, name='name-9', age=29}}
例子2:
代码:
public static void toMapTest(List<String> list){ Map<String,String> map = list.stream().limit(3).collect(Collectors.toMap(e -> e.substring(0,1),e -> e)); Map<String,String> map1 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b)); Map<String,String> map2 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b,HashMap::new)); System.out.println(map.toString() + "\n" + map1.toString() + "\n" + map2.toString()); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); toMapTest(list); }
运行效果:
{1=123, 4=456, 7=789}
{a=asdaa, 1=1101, 2=2321eew, 3=3e3e3e, 4=456, 7=789}
{a=asdaa, 1=1101, 2=2321eew, 3=3e3e3e, 4=456, 7=789}
第一种方式中,如果不添加limit限制,就会抛出异常。
还有并发的版本:toConcurrentMap,同样三种重载方法,与toMap基本一致,只是它最后使用的map是并发Map:ConcurrentHashMap。
5.2、toCollection
流中的元素全部放置到一个集合中返回,这里使用Collection,泛指多种集合。
代码:
public static void toCollectionTest(List<String> list) { List<String> ll = list.stream().collect(Collectors.toCollection(LinkedList::new)); ll.stream().forEach(System.out::println); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); toCollectionTest(list); }
5.3、toList
将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList
代码:
public static void toListTest(List<String> list) { List<String> ll = list.stream().collect(Collectors.toList()); ll.stream().forEach(System.out::println); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); toListTest(list); }
5.4、toSet
将流中的元素放置到一个无序集set中去。默认为HashSet
代码:
public static void toSetTest(List<String> list) { Set<String> ss = list.stream().collect(Collectors.toSet()); ss.stream().forEach(System.out::println); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); toSetTest(list); }
5.5、joining
目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀
代码:
public static void joiningTest(List<String> list){ // 无参方法 String s = list.stream().collect(Collectors.joining()); System.out.println(s); // 指定连接符 String ss = list.stream().collect(Collectors.joining("-")); System.out.println(ss); // 指定连接符和前后缀 String sss = list.stream().collect(Collectors.joining("-","S","E")); System.out.println(sss); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); joiningTest(list); }
运行结果:
1234567891101212121121asdaa3e3e3e2321eew 123-456-789-1101-212121121-asdaa-3e3e3e-2321eew S123-456-789-1101-212121121-asdaa-3e3e3e-2321eewE
5.6、mapping
这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳
代码:
public static void mapingTest(List<String> list){ //3个可转Integer元素 List<Integer> ll = list.stream().limit(3).collect(Collectors.mapping(Integer::valueOf,Collectors.toList())); ll.stream().forEach(System.out::println); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); mapingTest(list); }
运行结果:
123 456 789
5.7、collectingAndThen
该方法是在归纳动作结束之后,对归纳的结果进行再处理
代码:
public static void collectingAndThenTest(List<String> list){ int length = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),e -> e.size())); System.out.println(length); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); collectingAndThenTest(list); }
运行结果:8
5.8、counting
该方法用于计数
代码:
public static void countingTest(List<String> list){ long size = list.stream().collect(Collectors.counting()); System.out.println(size); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); countingTest(list); }
运行结果:8
5.8、minBy/maxBy
生成一个用于获取最小/最大值的Optional结果的Collector
代码:
public static void maxByAndMinByTest(List<String> list){ System.out.println(list.stream().collect(Collectors.maxBy((a,b) -> a.length()-b.length()))); System.out.println(list.stream().collect(Collectors.minBy((a,b) -> a.length()-b.length()))); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); maxByAndMinByTest(list); }
运行结果:
Optional[212121121]
Optional[123]
5.9、summingInt/summingLong/summingDouble
生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和
代码:
public static void summingTest(List<String> list){
//参数的作用就是将元素转换为指定的类型,最后结果与转换后类型一致 int i = list.stream().limit(3).collect(Collectors.summingInt(Integer::valueOf)); long l = list.stream().limit(3).collect(Collectors.summingLong(Long::valueOf)); double d = list.stream().limit(3).collect(Collectors.summingDouble(Double::valueOf)); System.out.println(i +"\n" +l + "\n" + d); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); summingTest(list); }
运行结果:
1368 1368 1368.0
5.10、averagingInt/averagingLong/averagingDouble
生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型
代码:
public static void averagingTest(List<String> list){ double i = list.stream().limit(3).collect(Collectors.averagingInt(Integer::valueOf)); double l = list.stream().limit(3).collect(Collectors.averagingLong(Long::valueOf)); double d = list.stream().limit(3).collect(Collectors.averagingDouble(Double::valueOf)); System.out.println(i +"\n" +l + "\n" + d); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); averagingTest(list); }
运行结果:
456.0 456.0 456.0
5.11、reducing
reducing方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用
源码:
public final class Collectors { // 无初始值的情况,返回一个可以生成Optional结果的Collector public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) {/*...*/} // 有初始值的情况,返回一个可以直接产生结果的Collector public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {/*...*/} // 有初始值,还有针对元素的处理方案mapper,生成一个可以直接产生结果的Collector,元素在执行结果操作op之前需要先执行mapper进行元素转换操作 public static <T, U> Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op) {/*...*/} }
代码:
public static void reducingTest(List<String> list){ System.out.println(list.stream().limit(4).map(String::length).collect(Collectors.reducing(Integer::sum))); System.out.println(list.stream().limit(3).map(String::length).collect(Collectors.reducing(0, Integer::sum))); System.out.println(list.stream().limit(4).collect(Collectors.reducing(0,String::length,Integer::sum))); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); reducingTest(list); }
运行结果:
Optional[13]
9
13
5.12、groupingBy
这个方法是用于生成一个拥有分组功能的Collector,它也有三个重载方法
public final class Collectors { // 只需一个分组参数classifier,内部自动将结果保存到一个map中,每个map的键为?类型(即classifier的结果类型),值为一个list,这个list中保存在属于这个组的元素。 public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy( Function<? super T, ? extends K> classifier) {/*...*/} // 在上面方法的基础上增加了对流中元素的处理方式的Collector,比如上面的默认的处理方法就是Collectors.toList() public static <T, K, A, D>Collector<T, ?, Map<K, D>> groupingBy( Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) {/*...*/} // 在第二个方法的基础上再添加了结果Map的生成方法。 public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) {/*...*/} }
代码:
public static void groupingByTest(List<String> list){ Map<Integer,List<String>> s = list.stream().collect(Collectors.groupingBy(String::length)); Map<Integer,List<String>> ss = list.stream().collect(Collectors.groupingBy(String::length, Collectors.toList())); Map<Integer,Set<String>> sss = list.stream().collect(Collectors.groupingBy(String::length,HashMap::new,Collectors.toSet())); System.out.println(s.toString() + "\n" + ss.toString() + "\n" + sss.toString()); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); groupingByTest(list); }
运行结果:
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
5.13、partitioningBy
该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List
该方法有两个重载方法:
public final class Collectors { // 只需一个校验参数predicate public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {/*...*/} // 在上面方法的基础上增加了对流中元素的处理方式的Collector,比如上面的默认的处理方法就是Collectors.toList() public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream) {/*...*/} }
代码:
public static void partitioningByTest(List<String> list){ Map<Boolean,List<String>> map = list.stream().collect(Collectors.partitioningBy(e -> e.length()>5)); Map<Boolean,Set<String>> map2 = list.stream().collect(Collectors.partitioningBy(e -> e.length()>6,Collectors.toSet())); System.out.println(map.toString() + "\n" + map2.toString()); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); partitioningByTest(list); }
运行结果:
{false=[123, 456, 789, 1101, asdaa], true=[212121121, 3e3e3e, 2321eew]}
{false=[123, 456, 1101, 789, 3e3e3e, asdaa], true=[212121121, 2321eew]}
5.14、summarizingInt/summarizingLong/summarizingDouble
这三个方法适用于汇总的,返回值分别是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics。
在这些返回值中包含有流中元素的指定结果的数量、和、最大值、最小值、平均值。所有仅仅针对数值结果。
代码:
public static void summarizingTest(List<String> list){ IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(String::length)); LongSummaryStatistics longSummary = list.stream().limit(4).collect(Collectors.summarizingLong(Long::valueOf)); DoubleSummaryStatistics doubleSummary = list.stream().limit(3).collect(Collectors.summarizingDouble(Double::valueOf)); System.out.println(intSummary.toString() + "\n" + longSummary.toString() + "\n" + doubleSummary.toString()); } public static void main(String[] args) { List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew"); summarizingTest(list); }
运行结果:
IntSummaryStatistics{count=8, sum=40, min=3, average=5.000000, max=9}
LongSummaryStatistics{count=4, sum=2469, min=123, average=617.250000, max=1101}
DoubleSummaryStatistics{count=3, sum=1368.000000, min=123.000000, average=456.000000, max=789.000000}
Collectors总结:
整个Collectors工具类就是在为Collector服务,用于创建各种不同的Collector。部分功能与Stream中的方法重合了,为了简化代码,完全不必采用Collectors实现,优先Stream方法
更多请参考:
Java基础系列-Stream:https://www.jianshu.com/p/3dc56886c2eb
Java 8 中的 Streams API 详解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html
浙公网安备 33010602011771号