Java中的流可以定义为来自支持对其进行聚合操作的源的元素序列。这里的源是指向流提供数据的集合或数组。
流保持数据在源中的顺序。聚合操作或批量操作是允许我们轻松、清晰地表示流元素上的常见操作的操作。
在继续之前,重要的是要了解java 8流的设计方式使得大多数流操作只返回流。这有助于我们创建流操作链。这称为管道内衬。在这篇文章中,我将多次使用该术语,因此请牢记。
1. Java流与集合
我们所有人都在youtube或其他此类网站上观看在线视频。当您开始观看视频时,首先会将一小部分文件加载到计算机中并开始播放。开始播放之前,您无需下载完整的视频。这称为流式传输。我将尝试将此概念与集合相关联,并与流区分开来。
在基本级别上,集合和流之间的差异与计算内容的时间有关。集合是内存中的数据结构,它保存数据结构当前具有的所有值,必须先计算集合中的每个元素,然后才能将其添加到集合中。流是概念上固定的数据结构,其中的元素是按需计算的。这带来了显着的编程优势。其思想是,用户将只从流中提取他们需要的值,并且这些元素只在用户需要时以不可见的方式生成。这是生产者与消费者关系的一种形式。
在Java中,java.util.stream表示可以对其执行一个或多个操作的流。流操作可以是中间操作,也可以是终端操作。当终端操作返回某种类型的结果时,中间操作返回流本身,这样您就可以在一行中链接多个方法调用。流是在源上创建的,例如JavaUTL集合,如列表或集合(不支持映射)。流操作可以顺序执行,也可以并行执行。
基于以上几点,如果我们列出流的各种特性,它们将如下所示:
不是数据结构
专为lambdas设计
不支持索引访问
可以轻松输出为数组或列表
支持延迟访问
可并行化
2.创建流的不同方法
下面是从集合构建流的最流行的不同方法。
2.1 Stream.of(val1,val2,val3 ....)
public class StreamBuilders{ public static void main(String[] args) { Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9); stream.forEach(p -> System.out.println(p)); }}
`
2.2 Stream.of(arrayOfElements)
public class StreamBuilders{ public static void main(String[] args) { Stream<Integer> stream = Stream.of( new Integer[]{1,2,3,4,5,6,7,8,9} ); stream.forEach(p -> System.out.println(p)); }}
`
2.3 List.stream()
public class StreamBuilders
{
public static void main(String[] args)
{
List<Integer> list = new ArrayList<Integer>();
for(int i = 1; i< 10; i++){
list.add(i);
}
Stream<Integer> stream = list.stream();
stream.forEach(p -> System.out.println(p));
}
}
`
2.4 Stream.generate()或Stream.iterate()
public class StreamBuilders{ public static void main(String[] args) { Stream<Date> stream = Stream.generate(() -> { return new Date(); }); stream.forEach(p -> System.out.println(p)); }}
`
2.5 字符串字符或字符串令牌
public class StreamBuilders
{
public static void main(String[] args)
{
IntStream stream = "12345_abcdefg".chars();
stream.forEach(p -> System.out.println(p));
//OR
Stream<String> stream = Stream.of("A$B$C".split("\\$"));
stream.forEach(p -> System.out.println(p));
}
}
`
除了上面的列表之外,还有其他一些方法,比如使用stream.buider或使用中间操作。我们将不时在单独的帖子中了解它们。
3.将流转换为集合
我宁愿说将流转换为其他数据结构。
!image-20191008224611545
3.1 将流转换为列表– Stream.collect(Collectors.toList())
public class StreamBuilders { public static void main(String[] args){ List<Integer> list = new ArrayList<Integer>(); for(int i = 1; i< 10; i++){ list.add(i); } Stream<Integer> stream = list.stream(); List<Integer> evenNumbersList = stream.filter(i -> i%2 == 0).collect(Collectors.toList()); System.out.print(evenNumbersList); }}
`
3.2 将Stream转换为数组– Stream.toArray(EntryType [] :: new)
public class StreamBuilders { public static void main(String[] args){ List<Integer> list = new ArrayList<Integer>(); for(int i = 1; i< 10; i++){ list.add(i); } Stream<Integer> stream = list.stream(); Integer[] evenNumbersArr = stream.filter(i -> i%2 == 0).toArray(Integer[]::new); System.out.print(evenNumbersArr); }}
`
还有很多其他方式可以将流收集到集合,映射或多种方式中。只有通过收集类,试着记住他们
4.核心流操作
流抽象为您提供了一长串有用的函数。我不打算全部报道,但我计划在这里列出所有最重要的,你必须知道第一手资料。
在继续之前,让我们事先构建String的集合。我们将在此列表中构建示例,以便易于关联和理解。
List<String> memberNames = new ArrayList<>();memberNames.add("Amitabh");memberNames.add("Shekhar");memberNames.add("Aman");memberNames.add("Rahul");memberNames.add("Shahrukh");memberNames.add("Salman");memberNames.add("Yana");memberNames.add("Lokesh");
`
这些核心方法分为以下两个部分:
4.1 中间作业
中间操作返回流本身,这样就可以在一行中链接多个方法调用。
4.1.1 Stream.filter()
filter接受一个谓词来过滤流的所有元素。
此操作是中间操作,使我们能够对结果调用另一个流操作(例如foreach)。
memberNames.stream().filter((s) -> s.startsWith("A"))
.forEach(System.out::println);
Output:
Amitabh
Aman
`
4.1.2. Stream.map()
中间操作映射通过给定的函数将每个元素转换为另一个对象。下面的示例将每个字符串转换为大写字符串。但是,您也可以使用map将每个对象转换为另一种类型。
memberNames.stream().filter((s) -> s.startsWith("A"))
.map(String::toUpperCase)
.forEach(System.out::println);
Output:
AMITABH
AMAN
`
4.1.3 Stream.sorted()
sorted是返回流的已排序视图的中间操作。除非您通过自定义比较器,否则元素将以自然顺序排序。
memberNames.stream().sorted()
.map(String::toUpperCase)
.forEach(System.out::println);
Output:
AMAN
AMITABH
LOKESH
RAHUL
SALMAN
SHAHRUKH
SHEKHAR
YANA
`
请记住,sorted只创建流的已排序视图,而不处理备份集合的排序。memberNames的顺序保持不变。
4.2 终端操作
终端操作返回某种类型的结果,而不是流。
4.2.1 Stream.forEach()
此方法有助于迭代流的所有元素,并对每个元素执行一些操作。该操作作为lambda表达式参数传递。
memberNames.forEach(System.out::println);
`
4.2.2. Stream.collect()
collect()方法,用于从Steam接收元素并将其存储在集合中,并在参数函数中提到。
List<String> memNamesInUppercase = memberNames.stream().sorted()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.print(memNamesInUppercase);
Outpout: [AMAN, AMITABH, LOKESH, RAHUL, SALMAN, SHAHRUKH, SHEKHAR, YANA]
`
4.2.3 Stream.match()
可以使用各种匹配操作来检查某个谓词是否与流匹配。所有这些操作都是终端操作,并返回布尔结果。
boolean matchedResult = memberNames.stream()
.anyMatch((s) -> s.startsWith("A"));
System.out.println(matchedResult);
matchedResult = memberNames.stream()
.allMatch((s) -> s.startsWith("A"));
System.out.println(matchedResult);
matchedResult = memberNames.stream()
.noneMatch((s) -> s.startsWith("A"));
System.out.println(matchedResult);
Output:
true
false
false
`
4.2.4 Stream.count()
count是一个终端操作,将流中的元素数返回为long。
long totalMatched = memberNames.stream()
.filter((s) -> s.startsWith("A"))
.count();
System.out.println(totalMatched);
Output: 2
`
4.2.5 Stream.reduce()
此终端操作使用给定函数对流的元素执行缩减。结果是一个可选的保持减少值。
Optional<String> reduced = memberNames.stream()
.reduce((s1,s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
Output: Amitabh#Shekhar#Aman#Rahul#Shahrukh#Salman#Yana#Lokesh
`
5.流短路操作
尽管流操作是在满足谓词的集合中的所有元素上执行的,但是每当在迭代过程中遇到匹配的元素时,通常需要中断该操作。在外部迭代中,将使用if-else块。在内部迭代中,可以使用某些方法来实现此目的。我们来看两个这样的方法的示例:
5.1 Stream.anyMatch()
当作为谓词传递的条件满足时,将返回true。它不会再处理任何元素。
boolean matched = memberNames.stream()
.anyMatch((s) -> s.startsWith("A"));
System.out.println(matched);
Output: true
`
5.2 Stream.findFirst()
它将从流返回第一个元素,然后不再处理任何元素。
String firstMatchedName = memberNames.stream()
.filter((s) -> s.startsWith("L"))
.findFirst().get();
System.out.println(firstMatchedName);
Output: Lokesh
`
6. Java Steam中的并行性
在java se 7中添加fork/join框架后,我们有了在应用程序中实现并行操作的高效机制。但是,实现此框架本身是一项复杂的任务。如果做得不好,是可能导致应用程序崩溃的复杂多线程错误的来源。通过引入内部迭代,我们可以并行执行操作。
要启用并行性,您要做的就是创建并行流,而不是顺序流。令您惊讶的是,这确实非常容易。在上面列出的任何一个流示例中,只要您想在并行内核中使用多个线程执行特定的作业,您只需调用方法parallel stream()method而不是stream()method。
public class StreamBuilders { public static void main(String[] args){ List<Integer> list = new ArrayList<Integer>(); for(int i = 1; i< 10; i++){ list.add(i); } //Here creating a parallel stream Stream<Integer> stream = list.parallelStream(); Integer[] evenNumbersArr = stream.filter(i -> i%2 == 0).toArray(Integer[]::new); System.out.print(evenNumbersArr); }}
`
这项工作的主要推动力是使并行性更易于开发人员使用。尽管Java平台已经为并发和并行性提供了强大的支持,但是开发人员在根据需要将其代码从顺序迁移到并行时会遇到不必要的障碍。因此,重要的是要鼓励顺序友好和并行友好的习语。通过将重点转移到描述应该执行什么计算,而不是应该如何执行计算,可以方便地进行操作。
同样重要的是要在使并行性变得更容易但不至于使其变得不可见之间取得平衡。使并行性透明将引入不确定性,并可能在用户可能不期望的情况下进行数据竞争。
这就是我想要分享的关于Java8中引入的流抽象的基础知识。我将在以后的文章中讨论与Streams相关的其他各种事情。
java培训:http://www.baizhiedu.com/java2019