所有java Collection
都有 stream()
和 parallelStream()
方法可以从中构造一个 Stream
:
Collection<String> stringList = new ArrayList<>(); Stream<String> stringStream = stringList.parallelStream();
可以使用以下两种方法之一从数组创建 Stream
:
String[] values = { "aaa", "bbbb", "ddd", "cccc" }; Stream<String> stringStream = Arrays.stream(values); Stream<String> stringStreamAlternative = Stream.of(values);
Arrays.stream()
和 Stream .of()
不同之处在于 Stream.of()
有一个varargs参数,因此可以像下面这样使用:
Stream<Integer> integerStream = Stream.of(1, 2, 3);
还有一些primitive(原始的,原函数,看下面例子便知道什么意思了) Stream
s,你可以使用。 例如:
IntStream intStream = IntStream.of(1, 2, 3); DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);
这些primitive streams (原始流)也可以使用 Arrays.stream()
方法构造:
IntStream intStream = Arrays.stream(new int[]{ 1, 2, 3 });
可以从具有指定范围的数组创建一个 Stream
。
int[] values= new int[]{1, 2, 3, 4, 5}; IntStream intStram = Arrays.stream(values, 1, 3);
注意任何primitive streams (原始流)可以使用 boxed
方法转换为boxed类型流:
Stream<Integer> integerStream = intStream.boxed();
这在某些情况下可能是有用的,如果你想收集数据,因为primitive streams (原始流)没有任何可以需要一个 Collector
来作为参数的 collect
方法。
注意需
import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; List<Integer> list = IntStream.range(1, 100).boxed().collect(Collectors.toList()); System.out.println(list.stream().count());
Double avarage = list.stream().collect(Collectors.averagingInt(item -> item));
List<Integer> list = IntStream.range(1, 100).boxed().collect(Collectors.toList()); IntSummaryStatistics iss = list.stream().collect(Collectors.summarizingInt(value -> value)); System.out.println(iss);
输出结果:
IntSummaryStatistics{count=99, sum=4950, min=1, average=50.000000, max=99}
List<Integer> list = IntStream.range(1, 100).boxed().collect(Collectors.toList()); Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(p -> p, q->q*3)); System.out.println(map);
输出结果:
{1=3, 2=6, 3=9, 4=12, 5=15, 6=18, 7=21, 8=24, 9=27, 10=30, 11=33, 12=36, 13=39, 14=42, 15=45, 16=48, 17=51, 18=54, 19=57, 20=60, 21=63, 22=66, 23=69, 24=72, 25=75, 26=78, 27=81, 28=84, 29=87, 30=90, 31=93, 32=96, 33=99, 34=102, 35=105, 36=108, 37=111, 38=114, 39=117, 40=120, 41=123, 42=126, 43=129, 44=132, 45=135, 46=138, 47=141, 48=144, 49=147, 50=150, 51=153, 52=156, 53=159, 54=162, 55=165, 56=168, 57=171, 58=174, 59=177, 60=180, 61=183, 62=186, 63=189, 64=192, 65=195, 66=198, 67=201, 68=204, 69=207, 70=210, 71=213, 72=216, 73=219, 74=222, 75=225, 76=228, 77=231, 78=234, 79=237, 80=240, 81=243, 82=246, 83=249, 84=252, 85=255, 86=258, 87=261, 88=264, 89=267, 90=270, 91=273, 92=276, 93=279, 94=282, 95=285, 96=288, 97=291, 98=294, 99=297}
List<Integer> list = new Random().ints(-100,100).limit(250).boxed().collect(Collectors.toList()); Optional<Integer> max = list.stream().reduce(Math::max); max.ifPresent(value -> System.out.println(value));
应该有些理解了吧。
当终端操作被调用时,流被关闭。当我们的需求只有发生在终端操作的改变时,可以 重复使用中间操作流。 我们可以创建 a stream supplier(一个流供应者)来构造一个已经建立了所有中间操作的新流。
Supplier<Stream<String>> streamSupplier = () -> Stream.of("apple", "banana","orange", "grapes", "melon","blueberry","blackberry") .map(String::toUpperCase).sorted(); streamSupplier.get().filter(s -> s.startsWith("A")).forEach(System.out::println); // APPLE streamSupplier.get().filter(s -> s.startsWith("B")).forEach(System.out::println); // BANANA // BLACKBERRY // BLUEBERRY
int []
数组可以使用流转换为 List
int[] ints = {1,2,3}; List<Integer> list = IntStream.of(ints).boxed().collect(Collectors.toList());
通过 flatMap()来扁平化处理流
A Stream
of items that are in turn streamable can be flattened into a single continuous Stream
:
flatMap:和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中
map和flatMap方法示意图:
可以看出map只转换
可以看出flatMap不仅转换,又进一步合并了一下,将多个子Stream合并为一个Stream。
由下面例子可以看出,大的 Stream
中的子 Stream
可以被扁平化处理为单个连续的 Stream
:
Map<String, List<Integer>> map = new LinkedHashMap<>(); map.put("a", Arrays.asList(1, 2, 3)); map.put("b", Arrays.asList(4, 5, 6)); List<Integer> allValues = map.values() // Collection<List<Integer>> .stream() // Stream<List<Integer>> .flatMap(List::stream) // Stream<Integer> .collect(Collectors.toList()); System.out.println(allValues); // [1, 2, 3, 4, 5, 6]
含 Map
的 List
可以被扁平化处理成一个连续的 Stream
:
List<Map<String, String>> list = new ArrayList<>(); Map<String,String> map1 = new HashMap(); map1.put("1", "one"); map1.put("2", "two"); Map<String,String> map2 = new HashMap(); map2.put("3", "three"); map2.put("4", "four"); list.add(map1); list.add(map2); Set<String> output= list.stream() // Stream<Map<String, String>> .map(Map::values) // Stream<List<String>> .flatMap(Collection::stream) // Stream<String> .collect(Collectors.toSet()); //Set<String> [one, two, three,four]
Stream
s,尤其是 IntStream
s,是一种实现求和项(Σ)的优雅方法。 Stream
可以用作求和的的范围边界。
E.g., Madhava的Pi近似值由公式给出(Source: wikipedia ):
这可以以任意精度计算。 例如,101项次幂:
double pi = Math.sqrt(12) * IntStream.rangeClosed(0, 100) .mapToDouble(k -> Math.pow(-3, -1 * k) / (2 * k + 1)) .sum();
Note:使用 double
的精度,选择29的上限就足以获得与 Math.Pi
大概一致的结果.
stream
s的元素通常不允许访问当前项的索引值。 要通过访问索引来迭代数组或 ArrayList
,使用 IntStream.range(start,endExclusive)
。
String[] names = { "Jon", "Darin", "Bauke", "Hans", "Marc" }; IntStream.range(0, names.length) .mapToObj(i -> String.format("#%d %s", i + 1, names[i])) .forEach(System.out::println);
range(start,endExclusive)
方法返回另一个 ÌntStream
并和 mapToObj(mapper)
返回一个String。
Output:
这非常类似于使用带有计数器的正常的for循环,但是具有流水线和并行化的优点:
for (int i = 0; i < names.length; i++) { String newName = String.format("#%d %s", i + 1, names[i]); System.out.println(newName); }
变量声明示例:
Collection<String> abc = Arrays.asList("a", "b", "c"); Collection<String> digits = Arrays.asList("1", "2", "3"); Collection<String> greekAbc = Arrays.asList("alpha", "beta", "gamma");
final Stream<String> concat1 = Stream.concat(abc.stream(), digits.stream()); concat1.forEach(System.out::print); // prints: abc123
final Stream<String> concat2 = Stream.concat( Stream.concat(abc.stream(), digits.stream()), greekAbc.stream()); System.out.println(concat2.collect(Collectors.joining(", "))); // prints: a, b, c, 1, 2, 3, alpha, beta, gamma
或者为了简化嵌套的 concat()
语法我们也可以使用 flatMap()
:
final Stream<String> concat3 = Stream.of( abc.stream(), digits.stream(), greekAbc.stream()) .flatMap(s -> s); // or `.flatMap(Function.identity());` (java.util.function.Function) System.out.println(concat3.collect(Collectors.joining(", "))); // prints: a, b, c, 1, 2, 3, alpha, beta, gamma
在从重复连接构造 Stream
s 时要小心,因为访问深度并置的 Stream
的元素可能导致深层调用链或者甚至是 StackOverflowException
(本就是栈操作)。
Java没有 Char Stream ,所以当使用 String
s并构造一个 Character
的 Character
s时,一个选项是使用 String.codePoints()
方法获取一个 IntStream
,所以 IntStream
可以得到如下:
publicIntStreamstringToIntStream(String in){ return in.codePoints(); }
更多的涉及做其他方式的转换,即IntStreamToString。 可以这样做:
publicStringintStreamToString(IntStream intStream){ return intStream.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString(); }
通过方法引用来创建具有帅气风格的文档化代码,使用带有Stream的方法引用使得复杂的过程易于阅读和理解(所以才说流程)。 考虑下面的代码:
public interfaceOrdered{ defaultintgetOrder(){ return 0; } } public interfaceValued<VextendsOrdered>{ booleanhasPropertyTwo(); VgetValue(); } public interfaceThing<VextendsOrdered>{ booleanhasPropertyOne(); Valued<V>getValuedProperty(); } public <V extends Ordered> List<V>myMethod(List<Thing<V>> things){ List<V> results = new ArrayList<V>(); for (Thing<V> thing : things) { if (thing.hasPropertyOne()) { Valued<V> valued = thing.getValuedProperty(); if (valued != null && valued.hasPropertyTwo()){ V value = valued.getValue(); if (value != null){ results.add(value); } } } } results.sort((a, b)->{ return Integer.compare(a.getOrder(), b.getOrder()); }); return results; }
最后使用 Stream
s和方法引用重写的自定义的方法更易读,而且每个步骤都很容易理解 - 它不仅更短,还显示了哪些接口和类负责每个 步:
public <V extends Ordered> List<V>myMethod(List<Thing<V>> things){ return things.stream() .filter(Thing::hasPropertyOne) .map(Thing::getValuedProperty) .filter(Objects::nonNull) .filter(Valued::hasPropertyTwo) .map(Valued::getValue) .filter(Objects::nonNull) .sorted(Comparator.comparing(Ordered::getOrder)) .collect(Collectors.toList()); }
未完待续:
部分参考示图源自: http://ifeve.com/stream/
部分示例源自: http://blog.csdn.net/chszs/article/details/47038607