前言
刚上大学的时候,JDK1.8就已经发布了,老师口口声声说应该激进些上1.8,可是等我快要工作的时候才发现1.8中Stream与λ表达式表达式的妙用。记录一下帮助记忆。
Stream接口
java.util.Stream
表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。 Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类:List、Set,但不支持Map。Stream的操作可以串行执行(stream)或者并行执行(parallelStream)。串行Stream上的操作是在一个线程中依次完成并行Streams,而并行Stream则是在多个线程上同时执行。
创建
由集合创建
可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建Stream。
// 为集合创建串行流
public static<T> Stream<T> stream()
// 为集合创建并行流
public static<T> Stream<T> parallelStream()
由数组创建
// Java8 中的 Arrays 的静态方法 stream() 可以获取数组流。
static <T> Stream<T> stream(T[] array)
// 重载形式,能够处理对应基本类型的数组
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
由值创建流
// 可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
public static<T> Stream<T> of(T... values)
由函数创建流
// 创建无限流
// 可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
// 迭代
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
// 生成
public static<T> Stream<T> generate(Supplier<T> s)
中间操作(返回的还是Stream)
中间操作,所以我们可以在过滤后的结果来应用其他Stream操作,主要方法包括:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered。
filter 过滤
过滤通过一个predicate接口来过滤并只保留符合条件的元素。
sort 排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据是不会被修改的。
map 映射(mapToInt, flatMap 等)
中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
distinct(去重)
对于流中的基本类型和引用对象类型都可以进行去重。
但值得注意的是引用对象的去重,引用对象要实现hashCode和equal方法,否则去重无效。
match 匹配
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。anyMatch、noneMatch。
终止操作
此后不可再执行其他Stream操作,主要方法包括:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min。
count 计数
计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。
reduce 聚合
这是一个最终操作,允许通过指定的函数来讲stream中的多个元素聚合为一个元素,聚合后的结果是通过Optional接口表示的。
forEach(遍历)
forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。
Map的Stream新特性
前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。
Map map = new HashMap<>(16);
for (int i = 0; i < 10; i++) {
map.putIfAbsent(i, "i" + i);
}
map.forEach((id, val) -> System.out.println(val));
以上代码很容易理解, putIfAbsent 不需要我们做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。
下面的例子展示了map上的其他有用的函数:
map.computeIfPresent(3, (num, val) -> (String) val + num);
map.get(3); // i33
map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9); // false
map.computeIfAbsent(23, num -> num);
map.containsKey(23); // true
map.computeIfAbsent(3, num -> "has");
map.get(3); // i33
接下来展示如何在Map里删除一个键值全都匹配的项
map.remove(3, "i33");
map.get(3); // i3
map.remove(3, "i33");
map.get(3); // null
获得键值对,如果没有则使用设置的值
map.getOrDefault(42, "not found"); // not found
Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。
map.merge(9, "i9", (value, newValue) -> value.concat(newValue));
map.get(9); // i9
map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9); // i9concat
API
JDK1.8 谷歌翻译版
应用场景分析
##获取List中元素的某个属性的集合
//把userList中User的realName全部拉出来存放到realNameList中
realNameList = userList.stream().map(User::getRealName()).collect(Collectors.toList());
Array->List
方法 Arrays.asList(arrays),注意这里的List不可操作,这里的ArrayList是内部是一个内部实现类,并没有实现操作的方法。
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
//
}
list->map并去重
//默认值的一个数据
return new ArrayList<>(Collections.singleton(new NameVO()));
//转换成id,对象,并去重
LinkedHashMap<Integer, SysDept> deptMap = listByIds(collect).stream().collect(
Collectors.toMap(SysDept::getDeptId, Function.identity(),
(key1, key2) -> key2, LinkedHashMap::new));
List转Array数组(原来方式)
String[] strs = (String[]) lists.toArray(new String[0]);
可以看出,原来是再toArray方法里面新建一个数组,<T> T[] toArray(T[] a)
;这个方法里面有个小技巧:
- 新建的数组的size小于等于list大小的话,list中所有元素都转化为数组中元素,且大小为数组大小
- 如果size比list元素个数大的话,则补充默认值
解决办法
String[] strs = (String[]) lists.toArray(new String[lists.size()]);
Stream流的toArray方法
获得Long[]类型数组
list.stream().map(Subject::getId).toArray(Long[]::new);
排序
//自定义排序规则:包含大小相等条件,日期(后-->前)
list.sort((o1, o2) -> o1.getTime().isBefore(o2.getTime()) ? 1 :
(o1.getTime().isAfter(o2.getTime()) ? -1 : 0));
同样也可以使用stream.sorted操作进行排序。sorted默认应该是hash值排序,也可以自定义Comparator规则
如上。