java 核心技术Ⅱ--章一:jdk8的流库使用

jdk8 流的介绍

流在表面看起来与集合很类似,都可以让我们转换和获取数据,但是还是有一些显著的差异。

  • 流并不存储元素。这些元素可能存储在底层的集合中或者是按需生成的。
  • 流的操作不会修改其数据源。例如:fillter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤的元素。
  • 流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。

下面为流的创建的几种方式:

public class CreatingStreams {

    public static <T> void show(String title,Stream<T> stream){

        final int SIZE = 10;
        //截取流的前10个元素,并将其作为list返回
        List<T> firstElements = stream.limit(SIZE+1).collect(Collectors.toList());
        System.out.print(title+":");
        for(int i=0;i<firstElements.size();i++){
            if(i>0){
                System.out.print(",");
            }if(i<SIZE){
                System.out.print(firstElements.get(i));
            }else{
                System.out.print("...");
            }
        }
        System.out.println();
    }

    //创建流的方式
    public static void main(String[] args) throws IOException {
        //方式1:Stream.of([])数组
        Path path = Paths.get("doc/test.txt");
        String contents = new String(Files.readAllBytes(path),StandardCharsets.UTF_8);
        Stream<String> words = Stream.of(contents.split("\\PL+")); 
        show("words",words);

        //方式2:Stream.of(不定参数)
        Stream<String> song = Stream.of("gently","down","the","stream");
        show("song",song);

        //方式3: 空流
        Stream<String> silence = Stream.empty();
        show("silence",silence);

        //方式4: 获取无限流
        Stream<String> echos = Stream.generate(() -> "Echo");
        show("echo",echos);

        //方式5:获取随机的无限流
        Stream<Double> randoms = Stream.generate(Math::random);
        show("randoms",randoms);

        //方式6:以种子的形式,递归的获取无限流
        Stream<BigInteger> integers = Stream.iterate(BigInteger.ONE, n -> n.add(BigInteger.ONE));
        show("integers",integers);

        //方式7:正则表达式
        Stream<String> wordsAnotherWay = Pattern.compile("\\PL+").splitAsStream(contents);
        show("wordsAnotherWay",wordsAnotherWay);

        //方式8:直接在在文件中获取
        try(Stream<String> lines = Files.lines(path,StandardCharsets.UTF_8)){
            show("lines",lines);
        }
    }
}

结果:

words:tjy,is,very,good,boy,tttt,iii,zz
song:gently,down,the,stream
silence:
echo:Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,...
randoms:0.45175494104727476,0.4205026378589467,0.6464438494443315,0.854943459150921,0.06889590801020251,0.866096917767406,0.1882725688303445,0.5673220279340991,0.17370889385858157,0.25846420406600223,...
integers:1,2,3,4,5,6,7,8,9,10,...
wordsAnotherWay:tjy,is,very,good,boy,tttt,iii,zz
lines:tjy is very good boy. tttt iii  zz
1、filter、map、flatMap方法
List<String> wordList = ...;
Stream<String> longWords = wordList.stream().filter(w -> w.length() > 12);

filter()方法的作用是,流中的每一个元素都使用传递的lambda表达式进行过滤,产生一个符合过滤条件的元素的新的流。其实此函数是Predicate接口中的test方法,但是一般使用lambda表达式。

Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);

map()方法的作用是,流中的每一个元素都使用传递的lambda表达式进行操作,产生一个包含了应用该函数后所产生的所有结果的流。

Stream<Stream<String>> result = words.stream().map(w -> letters(w));

其结果可能是:[…[”y”,”0”,”u”,”r”],[”’b”,”o”,”a”,”t”],…],为了将其摊平为字符流[…”y”,”o”,”u”,”r”,”b”,”o”,”a”,”t”,…]可以使用flatMap()方法。

Stream<String> result = words.stream().flatMap(w -> letters(w));
2、limit、skip、concat、distinct、sorted方法
Stream<Double> randoms = Stream.generate(Math::random).limit(100);

limit()方法产生一个参数值大小的流。该段代码的意思为产生一个100个随机数的流。

skip()方法与limit()方法相反,丢弃该前参数值大小的流。

Stream<String> combined = Stream.concat(letters("Hello"),letters("word"));

concat()方法是连接两个流。

distinct()方法会返回一个流,它的元素是从原有流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。

sorted()方法给流中元素排序。

3、简单约简

约简:对流进行一些简单的操作得到程序中可以使用的非流值。如int、String等数据结构的值。

count()方法:long count = words.stream().filter(w -> w.length() >= 3).count();

max()方法:Optional<String> largest = words.max(String::compareToIgnoreCase);

min()方法与max()方法类似。

findFirst()方法:Optional<String> startsWithQ = words.parallel().filter(s -> s.startWith("Q")).findFirst();

findAny()方法:不强调使用第一个匹配,而是使用任意的匹配都可以。

anyMatch()方法:Boolean aWordStartsWithQ = words.parallel().anyMath(s -> s.startsWith(“Q”));

allMatch()方法和noneMatch()方法,他们分别会在所有元素和没有任何元素匹配断言的情况下返回true。

reduce()方法:List<Integer> values = ...;Optional<Integer> sum = values.stream().reduce((x,y) -> x+y);

4、Optional类型

Optional 对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何的对象。

Optional<String> largest = words.max(String::compareToIgnoreCase);
//结果为空,采用默认值""
String result = largest.orElse("");
//通过代码计算默认值
String result = largest.orElseGet(() -> local.getDefault().getDisplayName());
//结果为空,抛出异常
String result = largest.orElseThrow(IllegalStateException::new);
//还有一些方法,请参照API文档
5、收集结果

将流元素的数据结构转化为我们熟知的集合等常用的数据结构。

//直接变量流数据
stream.forEach(System.out::println);
//转化为数组
String[] result = stream.toArray(String::new);
//转化为List集合
List<String> result = stream.collect(Collectors.toList());
//转化为Set集合
Set<String> result = stream.collect(Collectors.toSet());
//转化为特定的集合
TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
//转化为字符串
String result = stream.collect(Collectors.joining());
//转化为字符串,中间用“,”分离
String result = stream.collect(Collectors.joining(","));
//首先遍历元素,将不是字符串的转化为字符串。
String result = stream.map(Object::toString).collect(Collectors.joining());
//得到流的总和、平均值、最大、最小值等
IntSummaryStatistics summary = stream.collect(Collectors.summarizingInt(String::length));
double averageWordLength = summary.getAverage();
double maxWordLength = summary.getMax();
//将流转化为Map映射表
Map<Integer,String> idToName = people.collect(Collectors.toMap(Person::getId,Person::getName));
Map<Integer,Peoson> idToPeoson = people.collect(Collectors.toMap(Person::getId,Function.identity()));
//转化为Map映射表,若键值重复,会抛出异常,可以使用第三个函数引入元来解决
//该函数会针对已有值和新值来解决冲突并确定键对应的值,在下面的代码中,我直接用已有值来替换新值
Map<Integer,Peoson> idToPeoson = people.collect(Collectors.toMap(Person::getId,Function.identity(),(existingValue,newValue) -> existingValue));
6、基本类型流

基本类型流:IntStream、LongStream、DoubleStream.

short、char、byte、boolean等可以使用IntStream,float可以使用DoubleStream。

创建IntStream:

IntStream stream = IntStream.of(1,1,2,3,5);
stream = Arrays.stream(values,from,to);

基本对象流 <=>Stream ,但是使用基本类型流会更加高效,所以Stream有的方法,基本类型流也有,差异主要有以下几点:

toArray方法会返回基本类型数组。

  • 产生可选结果的方法会返回一个OptionalInt、OptionalLong或OptionalDouble,这些类与Optional类类似、但是具有getAsInt、getAsLong和getAsDouble方法而不是get方法。
  • 具有返回总和、平均值、最大值和最小值得sum、average、max、min方法。对象流没有定义这些方法。
  • summaryStatistics方法会产生一个类型为IntSummaryStatistics、LongSummaryStatistics或DoubleSummaryStatistics的对象,他们可以同时报告流的总和平均值、最大、最小值。
7、并行流

流使得并行处理块操作变得很容易,这个过程几乎是自动的,但是需要遵守一些规划,首先,必须有一个并行流。可以用Collection.paallelStream()方法从任何集合中获取一个并行流:

Stream<String> paralleWords = words.parallelStream();

使用parallel方法可以将任意的顺序流转换为并发流。

Stream<String> paralleWords = stream.of(wordArray).parallel();

只要在终结方法执行时,流处于并行模式,那么所有的中间流操作都将被并行化。

public class ParallelStreams {

    public static void main(String[] args) throws IOException {
        String contents = new String(Files.readAllBytes(Paths.get("doc/test.txt")),StandardCharsets.UTF_8);
        List<String> wordList = Arrays.asList(contents.split("\\PL+"));

        //错误的代码  多个线程竞争数组,导致错误
        int [] shortWords = new int[12];
        wordList.parallelStream().forEach(s -> { 
                    if(s.length() < 12 shortWords[s.length()]++;
                                            });
        System.out.println(Arrays.toString(shortWords));

        //正确的代码
        Map<Integer,Long> shortWordCount = wordList.parallelStream().filter(x -> x.length() < 12).collect(Collectors.groupingBy(String::length,Collectors.counting()));
        System.out.println(shortWordCount);

        //正确的代码 --另一种实现
        Map<Integer,List<String>> result = wordList.parallelStream().collect(Collectors.groupingByConcurrent(String::length));
        System.out.println(result.get(10).size());
    }

结果:

[0, 306, 1602, 1754, 1558, 792, 997, 512, 531, 214, 166, 72]
{1=312, 2=1752, 3=1944, 4=1704, 5=840, 6=1056, 7=528, 8=552, 9=216, 10=168, 11=72}
168

多次运行,发现每次运行第一种代码的执行结果都不一致。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值