JDK8-Stream
Stream流有一些特性
- Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
- 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
- Stream不保存数据,故每个Stream流只能使用一次。
使用Stream流,可以清楚地知道我们要对一个数据集做何种操作,可读性强。而且可以很轻松地获取并行化Stream流,不用自己编写多线程代码,可以让我们更加专注于业务逻辑。
怎么创建Stream类?
四种方式:
Stream<String> stringSteam = Stream.of("a", "bb", "ccc", "dddd", "abc", "abcd");
//输出:a,bb,ccc,dddd,abc,abcd
Stream stringSteam = Stream.builder().add("a").add("bb").add("ccc").build();
//输出:a,bb,ccc
Stream stringSteam = Stream.iterate(0, x -> x + 1).limit(3);
//输出:0,1,2
Stream<String> generate = Stream.generate(() -> "hello world").limit(3);
// 输出三个“hello world”
另外:数组可以通过Arrays类中的stream(...)方法转换成Stream,集合可以通过Collection接口中的stream()方法转换成Stream。
tring[] strs = new String[]{"a", "bb", "ccc"};
Stream<String> stream = Arrays.stream(strs);
List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("bb");
strList.add("ccc");
Stream<String> stream = strList.stream();
使用Stream类进行数据操作
allMatch
全部匹配
/**
* allMatch- 全部匹配
*/
@Test
public void testAllMatch() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
boolean isAllMatch = stringSteam.allMatch(item -> {
return item.contains("b");
});
System.out.println(isAllMatch);
}
结果:true
Filter
过滤操作,返回Stream,可以进行链式调用
stream-filter.png
/**
* Filter 过滤操作,返回Stream,可以进行链式调用
*/
@Test
public void testFilter() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
Stream filterStream = stringSteam.filter(item -> {
return item.contains("b");
});
filterStream.forEach(System.out::println);
}
结果:bd abc abcd
Map
对Stream里的元素都做一下映射操作,返回Stream
stream-map.png
/**
* Map -对Stream里的元素都做一下映射操作,返回Stream
*/
@Test
public void testMap() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
Stream mapSteam = stringSteam.map(item -> {
return item.toUpperCase();
});
mapSteam.forEach(System.out::println);
}
结果:BD ABC ABCD
flatMap
对Stream里的元素都做一下映射操作,元素映射操作返回的是Stream,最终返回Stream
stream-flatMap.png
/**
* flatMap - 对Stream里的元素都做一下映射操作,元素映射操作返回的是Stream,总终返回Stream
*/
@Test
public void testFlatMap() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
Stream mapSteam = stringSteam.flatMap(item -> {
return Arrays.stream(item.split(""));
});
mapSteam.forEach(System.out::println);
}
结果:b d a b c a b c d
concat
流连接操作
/**
* concat - 流连接操作
*/
@Test
public void testConcat() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
Stream.concat(Stream.of("bd", "abc"), Stream.of("e", "f")).forEach(System.out::println);
;
}
peek
生成一个包含原Stream的所有元素的新Stream,新Stream每个元素被消费之前都会执行peek给定的消费函数
/**
* peek-生成一个包含原Stream的所有元素的新Stream,新Stream每个元素被消费之前都会执行peek给定的消费函数
*/
@Test
public void testPeek() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
Stream<String> resultSteam = stringSteam.peek(item -> {
System.out.println("消费item->" + item);
});
// resultSteam在执行forEach里的Consumer之前时才会执行peek(惰性)
resultSteam.forEach(System.out::println);
}
skip
跳过前N个元素后,剩下的元素重新组成一个Stream
/**
* skip 跳过前N个元素后,剩下的元素重新组成一个Stream
*/
@Test
public void testSkip() {
Stream<String> stringSteam = Stream.of("bd", "abc", "abcd").skip(1);
stringSteam.forEach(System.out::println);
}
max, min
最大最小值
/**
* 最大最小值max, min
*/
@Test
public void testMax() {
Optional<String> optional = Stream.of("bd", "abc", "abcd").max((item1, item2) -> {
return Integer.valueOf(item1.length()).compareTo(Integer.valueOf(item2.length()));
});
System.out.println(optional.get());
}
reduce
累积器,将流中的元素两个一处理,返回结果
stream-reduce.png
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
第一个API的示例:
@Test
public void testReduce1() {
/**
* 两个元素相处理
*/
Optional<String> optional = Stream.of("bd", "abc", "abcd").reduce((item1, item2) -> {
System.out.println("item1=" + item1 + " item2=" + item2);
return item1 + "_" + item2;
});
System.out.println(optional.get());
}
结果:
item1=bd item2=abc
item1=bd_abc item2=abcd
bd_abc_abcd
第二个API就是在第一个API基础上加一个初始值
@Test
public void testReduce2() {
String optiona2 = Stream.of("bd", "abc", "abcd").reduce("init", (item1, item2) -> {
return item1 + "_" + item2;
});
System.out.println(optiona2);
}
结果:init_bd_abc_abcd
第三个API需要在并行的Stream中使用
@Test
public void testReduce3() {
List<String> itemList = new ArrayList<>();
itemList.add("bd");
itemList.add("abc");
itemList.add("abcd");
String optiona3 = itemList.parallelStream().reduce(
// 初始值
"init",
// 累加处理
(item1, item2) -> {
return item1 + "__" + item2;
},
// 将行并累计结果再合并
(e1, e2) -> {
return e1 + "##" + e2;
});
System.out.println(optiona3);
}
结果:init__bd##init__abc##init__abcd
Collect
暂时还没想好中文怎么解释...有两个API
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
第一个API主要用Collector
JDK8-Stream-Collectors
@Test
public void testCollect1() {
List<String> resutList = Stream.of("bd", "abc", "abcd").collect(Collectors.toList());
System.out.println(resutList);
}
第二个API的理解是:
第一个参数supplier 是一个生成目标类型实例的方法,第二个参数accumulator 将目标数据填充到第一步中,第三个参数combiner合并多个第二步的结果(只有并行Steam中生效)
@Test
public void testCollect2() {
/**
* 第一个参数supplier 是一个生成目标类型实例的方法,代表着目标容器是什么。
* 第二个参数accumulator 是将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中;
* 第三个参数combiner是将多个supplier生成的实例整合到一起的方法,代表着规约操作,将多个结果合并
*/
List<Person> listPerson = new ArrayList<>();
listPerson.add(new Person(10, "张三"));
listPerson.add(new Person(12, "李四"));
listPerson.add(new Person(14, "王五"));
Map<Long, Person> personMap = listPerson.stream().collect(
// 生成目标类型实例的方法
() -> {
return new HashMap<>();
},
// 将目标数据填充到第一步中
(hashMap, p) -> {
hashMap.put(p.getId(), p);
},
// 本例不是并行的stream,该方法没有实际意义
(hashMap, n) -> {
hashMap.putAll(n);
});
System.out.println(personMap);
}
输出:{10=Person(id=10, name=张三), 12=Person(id=12, name=李四), 14=Person(id=14, name=王五)}
@Test
public void testCollect3() {
/**
* 第一个参数supplier 是一个生成目标类型实例的方法,代表着目标容器是什么。
* 第二个参数accumulator 是将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中;
* 第三个参数combiner是将多个supplier生成的实例整合到一起的方法,代表着规约操作,将多个结果合并(并行操作中生效parallelStream)
*/
List<Person> listPerson = new ArrayList<>();
listPerson.add(new Person(10, "张三"));
listPerson.add(new Person(12, "李四"));
listPerson.add(new Person(14, "王五"));
Map<Long, Person> personMap = listPerson.parallelStream().collect(
// 生成目标类型实例的方法
() -> {
return new HashMap<>();
},
// 将目标数据填充到第一步中
(hashMap, p) -> {
hashMap.put(p.getId(), p);
},
// 将多个结果合并
(hashMap, n) -> {
hashMap.putAll(n);
});
System.out.println(personMap);
}
输出日志:
create 容器 HashMap
create 容器 HashMap
create 容器 HashMap
并且容器HashMap
并且容器HashMap
{10=Person(id=10, name=张三), 12=Person(id=12, name=李四), 14=Person(id=14, name=王五)}
除了Stream类,还有IntStream、DoubleStream、LongStream
,很显然这三种都是有针对性的流操作,分别针对int、double、long
类型的数据,它们都继承BaseStream类。