Stream流源码分析及技巧(含大量案例)

news2024/10/1 19:20:41

Stream流源码分析及技巧(含大量案例)

目录

Stream流源码分析及技巧(含大量案例)

更新说明

简介(这部分摘了部分官方文档)

特性

Stream接口关系图

 Stream流接口方法

Stream流之间的转换

与Stream流相关的文章

使用实例


更新说明

版本

编写时间

作者

说明

第一版

2023.2.14-2.15

蔡济民

初稿,JDK19,包含两个JDK16新特性

简介(这部分摘了部分官方文档)

支持顺序和并行聚合操作的元素序列

为了执行计算,流操作被组合到流管道中。流管道由源(可能是数组、集合、生成器函数、IO 通道等)、零个或多个中间操作(将一个流转换为另一个流,例如 filter(谓词))和一个终端操作(产生结果或副作用,例如 count() 或 forEach(Consumer))组成。流是懒惰的;仅在启动终端操作时对源数据执行计算,并且仅在需要时使用源元素。

流实现在优化结果计算方面允许很大的自由度。

与集合不同,集合更多的在于存储和取出,流更关注对元素的操作

流里面的元素:必须是非干扰的(它们不修改流源);并且在大多数情况下必须是无状态的(其结果不应依赖于在流管道执行期间可能更改的任何状态)。

一个流只能操作一次(调用中间流或终端流操作)。例如,这排除了“分叉”流,其中同一源馈送两个或多个管道,或同一流的多个遍历。流实现可能会引发IllegalStateException

流有一个 close() 方法并实现 AutoCloseable。在流关闭后对流进行操作将引发 IllegalStateException。

流管道可以按顺序或并行执行。此执行模式是流的属性。流是通过顺序执行或并行执行的初始选择创建的。(例如,Collection.stream() 创建一个顺序流,而 Collection.parallelStream() 创建一个并行流。这种执行模式的选择可以通过 sequential() 或 parallel() 方法修改,也可以使用 isParallel() 方法进行查询。

特性

stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。

stream不会改变数据源,通常情况下会产生一个新的集合或一个值。

stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

Stream接口关系图

祖先接口

AutoCloseable接口,提供了一个自动关闭的方法close()

父类接口

BaseStream接口,提供流之间的操作

比如生成一个流的关键在于确定如何求值下一项元素,所以就需要迭代器iterator。

需要拆分元素,返回此流的元素的拆分器。就需要spliterator,这是一个终端操作,为什么需要拆分,就是为部分元素计算整个流管道的代价过于昂贵。

是否并行执行就用到isParallel()

返回一个顺序流就用到sequential();同样返回无序流就是unorder();

parallel()返回一个并行流

onClose()提供关闭流的一个方法,是一个中间操作,返回具有附加关闭处理程序的等效流。

有了以上内容就可以思考,如何产生一个无穷的整数流呢

 Stream流接口方法

使用的比较多的我用蓝色加粗标注

Stream<T> filter(Predicate<? super T> predicate);过滤器,参数是逻辑表达式

       比如.filter(s->s!=1),就是取出不为1的流元素,成一个新流。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

将一个Stream映射到另外一个Stream,参数是接受一个函数,这个函数会被应用到所有元素上,将他们映射成一个新的元素。这是一个惰性求值的方法(就是用到的时候才会调用,这是来解决数据过于大的流,比如斐波那契数列等)

再补充一下惰性求值

比如

list.stream().filter(x -> {System.out.println(x);return true;});//这一段什么都不输出

但是在后面加上.count()后就会输出了,因为遇到最终操作才会真正拉动。

IntStream mapToInt(ToIntFunction<? super T> mapper);

MapToInt,将map转为IntStream流,举例如下:

List<String> list=new ArrayList<>();

Collections.addAll(list,"11","22","33");

这里用一个集合来装一系列字符串,如果我们想对它求和,直接使用list.stream().sum会发现没有这个方法,为什么呢,因为是String流,我们需要转换为int才可以处理。所以我们使用maptoInt,编写一个匿名内部类

list.stream().mapToInt(new ToIntFunction<String>() {

            @Override

            public int applyAsInt(String value) {

                return Integer.parseInt(value);

            }

        }).sum()

return那一行我们使用Integer的parseInt方法直接将字符串转为整数。我们可以用方法引用简写一下,就会很清晰list.stream().mapToInt(Integer::parseInt).sum(),到此就执行了一个字符串集合(存的是数字)的求和操作。

Maptodouble之类的类似理解就可。

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

这里要着重指明一下flatMap跟map之间的区别, flatMap方法返回的是一个stream,flatMap将流中的当前元素替换为此返回流拆解的流元素。相当于是一个扁平化操作。

这篇博文讲的例子是非常好的:

java8 stream流操作的flatMap(流的扁平化)_Mark_XC的博客-CSDN博客_stream().flatmap()

flatmaptoInt ,按照maptoInt类似理解就行。

default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper)

mapMulti,这是Java16引入的新方法,这点我看了几个例子,勉强理解了,他的用法是用少量(可能为零)的元素替换每个流元素时

Stream API 中的 mapMulti 指南

这篇文章写得不错

Java 16 中新增的 Stream 接口的一些思考 - 知乎

这篇文章我看了勉强理解了

Java 16 新方法 Stream.mapMult | w3c笔记

这篇文章举的大小写的例子,不需要flatMap后再嵌套Stream.of了

我目前理解地就是大小写替换会更简洁的写法,不用filter再多定义一个流,代码更简洁,并且stream里面就可以分组

我给出了以下两种例子,跟一种区别

Stream.of("Twix", "Snickers", "Mars")
                .mapMulti((s, c) -> {
                    c.accept(s.toUpperCase());
                    c.accept(s.toLowerCase());
                    c.accept(s.substring(0,3));
                })
                .forEach(System.out::println);

        List<String> list1 = Stream.of("Twix", "Snickers", "Mars")
                .flatMap(s -> Stream.of(s.toLowerCase()))
                .toList();

        List<String> list = Stream.of("Twix", "Snickers", "Mars")
                .<String>mapMulti((s, c) -> {
                    c.accept(s.toLowerCase());
                }).toList();

System.out.println(list1);
System.out.println(list);

Stream<T> distinct();

Distinct就是去重,这里有两点需要注意,Stream源码note中写的很好。

对于有序流,不同元素的选择是稳定的(对于重复的元素,将保留在遭遇顺序中首先出现的元素。对于无序流,不做稳定性保证。

在并行管道中保持 distinct() 的稳定性相对昂贵(要求操作充当完全屏障,具有大量的缓冲开销),并且通常不需要稳定性。使用无序流源(例如 generate(Supplier))或使用 unordered() 删除排序约束可能会导致并行管道中 distinct() 的执行效率显着提高,如果您的情况允许的话。如果需要与遭遇顺序保持一致,并且您在并行管道中使用 distinct() 时遇到性能或内存利用率不佳的情况,则切换到使用 sequential() 的顺序执行可能会提高性能。

Stream<T> sorted();

排序,按自然顺序排序,如果不可排序,会抛出ClassCastException异常

Stream<T> sorted(Comparator<? super T> comparator);

排序,按比较器的方式进行排序

Stream<T> peek(Consumer<? super T> action);

Peek是来返回流元素的,是一个中间方法,主要用于调试。

只有最终操作,类似于foreach的时候,元素才会被真正拉动,跟我们前面说的惰性求值一样

这里直接把源码这一段写的不错的贴过来

Stream.of("one", "two", "three", "four")    

 .filter(e -> e.length() > 3)     

.peek(e -> System.out.println("Filtered value: " +e))     

.map(String::toUpperCase)     

.peek(e -> System.out.println("Mapped value: " + e))    

.collect(Collectors.toList());

注意:这里是用于调试的,只有加上最终方法,peek里的输出才会输出

Stream<T> limit(long maxSize);

Limit就是取前n个元素,但是这里官方文档给了一点要注意的,我直接贴过来

虽然 limit() 在顺序流管道上通常是一个便宜的操作,但在有序并行管道上它可能非常昂贵,特别是对于 maxSize 的大值,因为 limit(n) 被约束为不仅返回任何 n 个元素,而且返回遭遇顺序中的前 n 个元素。如果您的情况的语义允许,使用无序流源(例如 generate(Supplier))或使用 unordered() 删除排序约束可能会导致并行管道中 limit() 的显著加速。如果需要与遭遇顺序保持一致,并且您在并行管道中使用 limit() 时遇到性能或内存利用率不佳的问题,则切换到使用 sequential() 的顺序执行可能会提高性能。

Stream<T> skip(long n);

同limit,取从n开始的元素,一样的问题

这一段我问了专家为什么stream流为什么会设计这么多提高性能的办法,比如惰性求值这种,这些方法不也就是求和,取元素之类的,但是String里面也有很多类似的方法,但是为什么不设计这些提高性能的方法呢。

老师说的是,String出现的比较早,stream流主要是方便我们代码编写,融合了很多数据类型,可能时间复杂度就更高。

我感觉可能还是不太理解,不过我找了这一篇关于stream流运行效率的问题

这个是实验结果

【Java】stream效率问题_嗨森bao的博客-CSDN博客_stream效率

这个是实验结果的总结

Java8 Stream 数据流,大数据量下的性能效率怎么样?_Java_xcbeyond_InfoQ写作社区

default Stream<T> takeWhile(Predicate<? super T> predicate) {

        Objects.requireNonNull(predicate);

一样在并行管道上会有时间效率问题

跟filter的区别,takeWhile 将在第一次出现不满足条件的项目时中止流。

filter 将从流中删除所有不满足条件的项目。

举例说明

Stream.of(1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1)

    .filter(i -> i < 4 )

.forEach(System.out::print);

打印123321

Stream.of(1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1)

    .takeWhile(i -> i < 4 )

.forEach(System.out::print);

打印123

Tropwhile同理takewhile

排除满足条件元素,直到不满足条件为止

void forEach(Consumer<? super T> action);

对每一个元素进行操作,是最终操作

官方文档写的不错

此操作的行为显然是不确定的。对于并行流管道,此操作不保证遵守流的遭遇顺序,因为这样做会牺牲并行性的好处。对于任何给定元素,可以在库选择的任何时间和任何线程中执行操作。如果操作访问共享状态,则它负责提供所需的同步。

void forEachOrdered(Consumer<? super T> action);

同理forEach不过是有序时候的操作

Object[] toArray();

<A> A[] toArray(IntFunction<A[]> generator);

将流中的元素转成数组存放,是最终操作,这里大概没有什么需要多说的

T reduce(T identity, BinaryOperator<T> accumulator);

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

Reduce是一个最终操作

这里直接贴官方文档的解释吧,讲的很清楚

reduction 操作(也称为fold),通过反复的对一个输入序列的元素进行某种组合操作(如对数的集合求和、求最大值,或者将所有元素放入一个列表),最终将其组合为一个单一的概要信息。stream类包含多种形式的通用reduction操作,如reduce和collect,以及其他多种专用reduction形式:sum,max或者count。

总和、最小值、最大值、平均值和字符串连接都是简化的特殊情况。对数字流求和可以表示为:整数和 = integers.reduce(0, (a, b) -> a+b);或:整数和 = 整数.reduce(0, 整数::总和);虽然与简单地改变循环中的运行总计相比,这似乎是一种更迂回的执行聚合的方式,但缩减操作可以更优雅地并行化,无需额外的同步,并且大大降低了数据争用的风险。

一般用法是这么用:

Integer sum = integers.reduce(0, (a, b) -> a+b);

Integer sum = integers.reduce(0, Integer::sum);

另外reduce可以并发计算,很强,并发计算的代码我忘记是哪篇文章了

这篇深一点,讲reduce功能的

一文讲透 Stream.reduce()_codingstyle的博客-CSDN博客_stream.reduce

这篇讲reduce使用例子的,有很多

https://www.cnblogs.com/gaohanghang/p/12390233.html

<R, A> R collect(Collector<? super T, A, R> collector);

主要是接收一个Collector实例,将流中元素收集成另外一个数据结构。

https://www.cnblogs.com/owenma/p/12207330.html

这篇文章写得太好了,直接看他总结的collect部分,还有实例,我写的练习实例在后面

collect方法是强制求值方法中,最复杂也最强大的接口,其作用是将流中的元素收集(collect)起来,并转化成特定的数据结构。

从函数式编程的角度来看,collect方法是一个高阶函数,其接受三个函数作为参数(supplieraccumulatorfinisher),最终生成一个更加强大的函数。在java中,三个函数参数以Collector实现对象的形式呈现。

supplier 方法:用于提供收集collect的初始值。

accumulator 方法:用于指定收集过程中,初始值和流中个体元素聚合的逻辑。

finnisher 方法:用于指定在收集完成之后的收尾转化操作(例如:StringBuilder.toString() ---> String)。

注意:Collectors工具类,很好的满足了平时常见的需求(Collector.toList()、Collctor.groupingBy())等

@SuppressWarnings("unchecked")

    default List<T> toList()

这里注解@SuppressWarnings("unchecked")的作用是 告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。

这个功能主要是将Stream流转List,是最终操作

Java16版本才可以使用。如下.toList()

Java8的话是使用.collect(Collectors.toList())

主要区别

toList()不能增删改,比如我写的下面这段代码

        //toList后,不能增删改

//        List<String> list = Stream.of("aaa", "bbb", "ccc").toList();

//        System.out.println(list.add("ddd"));

        //用Collectors.toList()之后就可以增删改了

        List<String> collect = Stream.of("aaa", "bbb", "ccc").collect(Collectors.toList());

        System.out.println(collect.add("ddd"));

一看就懂了

Collectors.toUnmodifiableList()是Collectors不能增删改的方法

list.stream().collect(Collectors.toUnmodifiableList());

这篇文章是专门讲toList跟Collectors.toList的性能的

Stream.toList()和Collectors.toList()的性能比较 - 知乎

Optional<T> min(Comparator<? super T> comparator);

就是求最小值的方法,注意:如果最小值为空时会抛出空指针异常

Max同理

long count();

就是流中元素的计数,这个很好理解,用官方文档这一段说明不错,等同于

mapToLong(e -> 1L).sum();

boolean anyMatch(Predicate<? super T> predicate);

另外两个放在一起说

anyMatch:判断的条件里,任意一个元素成功,返回true

allMatch:判断条件里的元素,所有的都是,返回true

noneMatch:与allMatch相反,判断条件里的元素,所有的都不是,返回true

Optional<T> findFirst();

返回流的第一个元素,注意:如果所选的元素为空,抛出空指针异常

Optional<T> findAny();

返回流中的任一一个元素

public static<T> Builder<T> builder() {

        return new Streams.StreamBuilderImpl<>();

}

返回一个Stream构造器,可以用build方法创建Stream

一般我们用Arrays.of,List.of或者Stream.of创建

这里可以使用Stream.Builder<String> builder = Stream.builder();

       Builder.add(“abc”);//这也是创建流的一种方式

只不过前两种一般更简洁一点

public static<T> Stream<T> empty() {

        return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);

}

创建空的Stream流,暂时忽略吧,我也不知道有啥大用

public static<T> Stream<T> of(T t)

返回单个元素顺序流,这个我们经常用到

Stream.of(“aaa”,”bbb”)这样的

Stream.ofNullable一起放在这里

返回包含单个元素的顺序流(如果非 null),否则返回空流。

public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

主要是用来 创建无数个有关系的元素的流

比如Stream.iterate("1",  b -> b+"0").limit(3).forEach(System.out::println);

就是1,10,100

再举个例子,斐波那契数列的例子

Stream.iterate(new int[]{0, 1}, n -> new int[]{n[1], n[0] + n[1]})    .limit(20)    .map(n -> n[0])    .forEach(x -> System.out.println(x));

这里还有一篇博客也是写的斐波那契数列

Stream.iterate() 生成 斐波那契数列(Fibonacci)_起名-困难户的博客-CSDN博客_stream.iterate

public static<T> Stream<T> generate(Supplier<? extends T> s)

通过generate()生成无限流数据,跟iterate有点像,但这个更加暴力,比如生成随机整数流

Stream.generate(() -> new Random().nextInt(10));

随机布尔流

Stream.generate(() -> new Random().nextBoolean())

   .forEach(e -> System.out.println(e));

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

将两个Stream流按顺序进行拼接成一个新的Stream流

Stream流之间的转换

Int类型的数组转为流的时候要进行装箱.boxed()

装箱之后可以通过collect(Collectors.toList())来转成List<Integer>

一样是.boxed()装箱后的数据通过.toArray(Integer:new)转为Integer

未装箱的数据IntStream流,或者List<Integer>流通过.mapToInt (Integer::valueOf).toArray();转为int[]

List< Integer > 通过list.toArray(new Integer[list.size()]);转成 Integer[ ]

转换 Int[] 至 String

String s = Arrays.stream(arr).boxed().map(i->i.toString()).collect(Collectors.joining());

与Stream流相关的文章

这篇是从0开始自己搭建一个Stream流,不是使用

https://www.cnblogs.com/xiaoxiongcanguan/p/10511233.html

这篇文章讲的也挺不错

【java基础】吐血总结Stream流操作_九离⠂的博客-CSDN博客

使用实例

以下案例均为本人写的代码,转载请标明出处

1.计算偶数个数,难度1星

给一个整型数组,统计该数组中偶数元素的个数, 返回结果

输入参数

输出结果

[2,1,2,3,4]

3

[2,2,0]

3

[1,3,5]

0

public int countEvens(int[] nums) {

        return (int) Arrays.stream(nums).filter(s -> s % 2 == 0).count();

}

2.幸运的1和3,难度1星

给定一个整型数组,如果数组不包含1和3,则返回true。

输入参数

输出结果

[0,2,4]

true

[1,2,3]

false

[1,2,4]

false

return Arrays.stream(arr).noneMatch(s->s==1||s==3);

3.相邻索引都是2,3星难度

给定一个整型数组,如果数组中出现两个相邻的索引中的元素都是2,则返回true

输入参数

输出结果

[1,2,2]

true

[1,2,1,2]

false

[2,1,2]

false

此题解析我写到例9去了

String collect = Arrays.stream(arr).boxed().map(i -> i.toString()).collect(Collectors.joining());

        return collect.matches(".*22.*");

4.圆周率N,难度3星

返回一个包含Pi(参考 Math.PI - π )的前n位数字的整数数组长度,n为方法接收的参数。

例如:n为3,则返回 [3,1,4]。

输入参数

输出结果

1

[3]

2

[3,1]

3

[3,1,4]

/*

            这一段是介绍下面的代码

            Arrays.stream(strings)这一步处理完是一个Stream,里面放了两个String 3 14159...

            我们需要把这两个String合并成一个String再转int再转数组输出

                .map(s -> s.split(""));

            将Stream<String>转成Stream<String[]>这样我们能对里面每个元素进行操作,但还是两个

                .flatMap(Arrays::stream)

            通过flatMap将元素扁平化,拉一起合并成一个Stream<String>

            .mapToInt(Integer::valueOf)

            转成IntStream流

            .limit(n)

            取前n个元素

            .toArray();

            转成int数组

            这个网址有图示化stream流

            https://blog.csdn.net/Mark_Chao/article/details/80810030

         */

public int[] makePiN(int n) {

        double pi = Math.PI;

        String pistr = String.valueOf(pi);

        //注意这里正则表达要写成\\.不然就是任意字符

        String[] strings = pistr.split("\\.");

        int[] arr =Arrays.stream(strings)

                .map(s -> s.split(""))

                .flatMap(Arrays::stream)

                .mapToInt(Integer::valueOf)

                .limit(n)

                .toArray();

        return arr;

}

5.数字是数组的长度,难度2星

给定义个整型数字(已知>=0),返回一个长度是该数字的整型数组,该数组中的元素按自然顺序递增, 例如数字是7,那么返回长度为7的整型数组:

数组的元素为[0,1,2,3,4,5,6]

输入参数

输出结果

4

[0,1,2,3]

1

[0]

10

[0,1,2,3,4,5,6,7,8,9]

//这个题主要是用到iterate迭代产生从0开始的自然数序列

public int[] fizzArray(int a) {

        return Stream.iterate(0, s->s+1).limit(a).mapToInt(Integer::intValue).toArray();

}

6.数字长度的字符串,难度2星

给一个任意的数字,返回一个长度是该数字的 字符串 数组,该数组中的元素按自然顺序递增,

例如数字是4,那么返回长度为4的字符串数组,数组的元素为 ["0","1","2","3"]

输入参数

输出结果

4

["0","1","2","3"]

10

["0","1","2","3","4","5","6","7","8","9"]

2

["0","1"]

public String[] fizzArray2(int a) {

        /*

            之前例题写过的就不写了

            .map(String::valueOf),对数字处理,转字符串

             0 1 2 3 4 5 ->Stream流上一共a个String类型,Stream<String>

            .toArray(String[]::new),把单个单个字符串转成字符串字符串数组,长度为a

         */

        return Stream.iterate(0, s -> s + 1).limit(a)

                .map(String::valueOf).toArray(String[]::new);

}

7.从开始到结束,难度2星

给定两个int数,一个是开始值一个是结束值, 返回一个数组。

这个数组是由开始值每一次递增1到结束的数据,数组中包含开始值,但不包含结束值。

注意:如果开始和结束相等,则返回一个空数组

输入参数

输出结果

510

[5,6,7,8,9]

1118

[11,12,13,14,15,16,17]

13

[1,2]

return Stream.iterate(0, s -> s + 1).limit(end).skip(start).mapToInt(Integer::intValue).toArray();

8.去掉最大和最小后的平均值,难度1星

给定一个整型数组,去掉数组中最大和最小的值, 返回其他元素的平均值

输入参数

输出结果

[1,2,3,4,100]

3

[1,1,5,5,10,8,7]

5

[-10,-4,-2,-4,-2,0]

-3

public int centeredAverage(int[] nums) {

        int sum=Arrays.stream(nums).sum();

        int min= Arrays.stream(nums).min().getAsInt();

        int max=Arrays.stream(nums).max().getAsInt();

        return  (sum-min -max )/(nums.length-2);

}

9.元素3间隔的出现3次,难度4星

给定一个整型数组,如果元素3在数组中 恰好 出现3次,并且中间都有一个其他元素则返回true, 例如[3, 1, 3, 2, 3]返回true

输入参数

输出结果

[3,1,3,1,3]

true

[3,1,3,3]

false

[3,4,3,3,4]

false

//这个题是有点质量的,需要思考一下int[]转string,这种转换并不常见,如果用 Arrays.toString会多出[, ]这些东西,流转换是用到Collectors.joining(),我们使用很少

public boolean haveThree(int[] arr) {

        if (Arrays.stream(arr).filter(s->s==3).count()!=3) {

            return false;

        }

        else {

            String s = Arrays.stream(arr).boxed().map(i->i.toString()).collect(Collectors.joining());

            System.out.println(s);

            return s.matches(".*3.3.3.*");

        }

}

此题类似例9,数组还有77,我不列出解答了

10.给定一个整型数组,如果数组包含两个彼此相邻的7,或者有两个7由一个元素分隔, 如

[7, 7]返回true,

[7,1,7]也要返回true;

否则返回false

还有一个类似的,数组含有12,思想都是一模一样的

给定一个整型数组,如果数组中有1元素, 并且在1元素后面的元素中还有2元素,

则返回true, 否则返回false.

11.最大与最小元素的差值,1星

给定一个整型数组(长度>=1),返回数组中最大值和最小值之间的差值。

输入参数

输出结果

[10,3,5,6]

7

[7,2,10,9]

8

[2,10,7,2]

8

public int bigDiff(int[] arr) {

        return Arrays.stream(arr).max().getAsInt()- Arrays.stream(arr).min().getAsInt();

   }

12.左移一位数,2星

给定一个整型的数组,将数组的元素左移一位后返回一个新的数组。

例如:[6,2,5,3] 返回 [2,5,3,6]

输入参数

输出结果

[6,2,5,3]

[2,5,3,6]

[1,2]

[2,1]

[1]

[1]

不难,懒得写注释了

public int[] shiftleft(int[] arr) {

        if(arr.length==0)   return new int[0];

        int[] ints = Stream.concat(Arrays.stream(arr).boxed(), Stream.of(arr[0]))

            .skip(1)

            .mapToInt(Integer::intValue).toArray();

        return ints;

}

13.之前的数组,3星

这个题我给三星的原因是很难想到takewhile()毕竟这个用的不多

给定一个非空的int类型的数组, 返回一个新数组。

新数组中存放的是原数组中从第一个元素开始直到第一个4为止(不包含)的数组。

如果第一个是4,则返回一个空数组,已知数组中一定存在4。

输入参数

输出结果

[1,2,4,1]

[1,2]

[3,1,4]

[3,1]

[1,4,4]

[1]

public int[] pre4(int[] nums) {

        if(nums[0]==4){

            return new int[0];

        }

        else

            return Arrays.stream(nums).takeWhile(i -> i != 4).toArray();

}

比一般的暴力做法简单多了。

15.1和4次数比较,2星

给定一个整型数组,如果1的出现次数大于4出现的次数,则返回true

输入参数

输出结果

[1,4,1]

true

[1,4,1,4]

false

[1,1]

true

public boolean more14(int[] arr) {

        int count1= (int) Arrays.stream(arr).filter(s->s==1).count();

        int count4= (int) Arrays.stream(arr).filter(s->s==4).count();

        if (count1>count4)  return true;

        else return false;

}

16.所有2的总和是8

给定一个整型数组,如果数组中所有2的总和为8,则返回true。

输入参数

输出结果

[2,3,2,2,4,2]

true

[2,3,2,2,4,2,2]

false

[1,2,3,4]

false

public boolean sum28(int[] arr) {

        if (Arrays.stream(arr).filter(s->s==2).sum()==8) {

            return true;

        }

        return false;

}

17.不存在1和4元素的数组,2星

给定一个整型数组,如果它同时包含1和4, 返回false,否则返回true;

输入参数

输出结果

[1,2,3]

true

[1,2,3,4]

false

[2,3,4]

true

public boolean no14(int[] arr) {

        if (Arrays.stream(arr).anyMatch(a->a==1)&& Arrays.stream(arr).anyMatch(a->a==4)) {

            return false;

        }

        return true;

}

18.2和4任意一个条件满足,4星

给定一个整型数组,如果数组中含有两个相邻的数字2,或是两个相邻的数字4,就返回true,否则返回false,

但是!如果数组既含有两个相邻的数字2又含有两个相邻的数字4,还返回false。

例如:([1, 2, 3, 2, 2, 4, 4]) -> false

这个也是前面的知识点应用,我给4星的原因是因为前面那个整形数组转字符串是4星,并且你不用Stream流+正则表达做,代码起码多10几行得再写个函数判断逻辑

public boolean either24(int[] arr) {

  String collect = Arrays.stream(arr).boxed().map(i -> i.toString()).collect(Collectors.joining());

        boolean istwo=collect.matches(".*22.*");

        boolean isfour=collect.matches(".*44.*");

        if(istwo&&isfour)   return false;

        else if(istwo||isfour)  return true;

        else return false;

}

19.把0移到前面,4.5星

给定一个int类型的数组, 将数组中的0都移到前面,其他值向后顺延

输入参数

输出结果

[1,0,0,1]

[0,0,1,1]

[0,1,1,0,1]

[0,0,1,1,1]

[1,0]

[0,1]

       此题给4.5星的原因是要想到0放前面,非0放后面,实际就是两个过滤的拼接,我感觉是不错,有技巧的。所以单独给它加了0.5星

public int[] zeroFront(int[] nums) {

        int[] ints = Stream.concat(Arrays.stream(nums).boxed().filter(s -> s == 0),

                        Arrays.stream(nums).boxed().filter(s -> s != 0))

                .mapToInt(Integer::intValue).toArray();

        return ints;

}

有个类似的题,把0移到后面

给定一个int类型的数组,先将数组中所有的10变成0, 然后将数组中的0都移到后面,其他的值向前顺延

不写样例跟代码了,一样的思路

20.4之后的数组,4星

给定一个非空的int类型的数组, 返回一个新数组。

新数组中存放的是原数组中从最后一个4开始到末尾的数组。

如果原数组中最后一个元素是4,则返回空数组。

已知:数组中一定存在4。

输入参数

输出结果

[2,4,1,2]

[1,2]

[4,1,4,2]

[2]

[4,4,1,2,3]

[1,2,3]

此题算是综合运用,但并不难

public int[] post4(int[] nums) {

        int index=-1;

        for (int i = nums.length-1; i >=0 ; i--) {

            if(nums[i]==4){

                index=i;

                break;

            }

        }

        //这里只需要调整下面的index+1就行

        int[] ints = Arrays.stream(nums).boxed().skip(index+1).mapToInt(Integer::intValue).toArray();

        return ints;

}

偶数在前,奇数在后,4星

给定一个int类型数组(长度任意), 将所有的偶数放到奇数的前面. 然后返回一个修改后的数组

注意:这个数组只有两个值重复出现。

输入参数

输出结果

[1,0,1,0,0,1,1]

[0,0,0,1,1,1,1]

[3,3,2]

[2,3,3]

[2,2,2]

[2,2,2]

       前面有个题跟此题思路也是一样的,所以我打分给他降了0.5星

return Stream.concat(Arrays.stream(nums).boxed().filter(s -> s % 2 == 0),

Arrays.stream(nums).boxed().filter(s -> s % 2 != 0))

.mapToInt(Integer::intValue).toArray();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348261.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OD面试经验分享,尤其注意机试题部分

文章目录招聘流程和背景介绍面试准备机试题目类型和解答技巧在算法部分在操作系统部分面试官提问和答题技巧面试总结和建议推荐一些华为 od 常见的机试题题目&#xff1a;两数之和题目&#xff1a;二叉树的遍历题目&#xff1a;链表反转题目&#xff1a;最大子序和招聘流程和背…

解决QML debugging is enabled.Only use this in a safe environment.警告

系列文章目录 文章目录系列文章目录前言一、警告原因二、解决办法参考前言 我试图运行一个非常简单的程序&#xff0c;当单击退出按钮时关闭窗口&#xff0c;但获取以下输出&#xff0c;前提是包含按钮的应用程序窗口不显示&#xff1a; 您已启用QML调试(实际上它默认启用)&…

CleanMyMac4.12.5最新版Mac系统清理优化工具

CleanMyMac X可以优化Mac系统。mac系统用久了&#xff0c;用CleanMyMac清理一下效果还不错。可用来清理系统的缓存、日志、语言和垃圾文件&#xff0c;还能卸载应用程序。小编给您带来cleanmymac中文版&#xff0c;CleanMyMac是一款Mac系统清理优化工具&#xff0c;使用只需两个…

Java中Synchronized关键字的基本使用方法

Java中Synchronized关键字的基本使用方法 1.简介 Synchronized是java的关键字&#xff0c;synchronized可以保证在同一个时刻&#xff0c;只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作)&#xff0c;synchronized可保证一个线程的变…

动态规划【Day01】| 669 · 换硬币、114 · 不同的路径、116 · 跳跃游戏

秘诀&#xff1a;确定状态转移方程初始条件和边界情况计算顺序 669 换硬币 669 换硬币 题目描述&#xff1a; 给出不同面额的硬币以及一个总金额. 写一个方法来计算给出的总金额可以换取的最少的硬币数量. 如果已有硬币的任意组合均无法与总金额面额相等, 那么返回 -1。 样…

元宇宙将如何彻底改变 K-12 和高等教育

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;9分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 想象一下&#xff0c;你将作为一个微小的细胞去参观人类的循环系统。这只是元宇…

基于django搭建简单的个人博客

文章目录第一步、在Ubuntu中安装虚拟环境并进入第二步、安装blog所需要的包&#xff0c;在requirements.txt中安装mysqlclient可能会报错&#xff0c;输入下列命令后在安装即可成功第三步、创建好数据库&#xff0c;把测试数据导入第四步、修改DjangoBlog包中 settings中数据库…

企业如何做好EHS环境健康安全管理?

目前随着传统制造业企业安全管理制度的落实&#xff0c;工人的安全意识得到很大的提升&#xff0c;但企业内部的安全管理制度并不能完全避免意外发生。如受限空间人员闯入、特种设备伤人、人员作业不规范、危化品泄露、仓储车间发生火情、有毒有害气体超标等一系列安全隐患。对…

leaflet 修改popup的样式,个性化弹窗(069)

第069个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中修改popup组件的样式,个性化弹窗。主要方法是更改css, 中增加custom-popup类名,style的样式要做穿透处理 >>>.具体方法请参考源代码。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实…

使用Python绘制股票CCI指标曲线

本文使用Python语言绘制一只股票的CCI&#xff08;Commodity channel index&#xff09;曲线&#xff0c;论文参考《Commodity channel index: Tool for trading cyclic trends》&#xff0c;该指标可以用来测量股价、外汇或者贵金属交易是否已超出常态分布范围&#xff0c;​ …

MYSQL数据库-主从复制(原理及搭建)

文章目录1 概述2 原理3 搭建3.1 主库配置3.2 从库配置1 概述 主从复制是指将主数据库的DDL和 DML操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行(也叫重做)&#xff0c;从而使得从库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进…

周立功ZCANPRO基础使用教程

软件及驱动安装 1.安装ZCANPRO软件 ZCANPRO软件官网:USB接口CAN卡-广州致远电子股份有限公司 点击资料下载: 2.安装驱动 本人使用的USBCANFD-200U设备,大家可根据自己的设备下载对应的驱动。 设备驱动官网链接:https://manual.zlg.cn/web/#/42/2462 软件使用教程 1.选择…

嵌入式开发:你需要知道的5种简单

传达嵌入式软件体系结构设计意图通常伴随着基于嵌入式开发人员经验的假设。你可以从资源受限的基于微控制器的系统的角度来看架构设计。如何设计架构将取决于系统的嵌入式软件分类。有许多不同的方法可以对嵌入式软件进行分类。我发现有五种简单的嵌入式软件分类可以帮助我调整…

字符串内存分配

涉及三块区域&#xff1a;栈&#xff0c;堆&#xff0c;字符串常量池&#xff08;jdk1.7之前在方法区&#xff0c;jdk1.7之后在堆中&#xff09; 关于字符串常量池到底在不在堆中&#xff1a; jdk1.6及以前&#xff0c;方法区独立存在&#xff08;不在堆里面&#xff09;&…

达梦数据库监控指标

一、达梦数据库监控指标 数据库常见性能指标主要有:当前登录数、非阻塞锁数、当前死锁数、阻塞锁数、当前锁数、会话数等内容,如下图所示。 注意:以下是 达梦数据库监控所需要的指标,具体根据需要监控哪些、超阈值而定。 1.1 新建主机群组 1.2 新建模板 1.3 创建主机 1.…

【Airplay_BCT】Bonjour API架构

Bonjour API 架构 OS X 和 iOS 为 Bonjour 服务应用程序提供了多层应用程序编程接口 (API)&#xff1a; Foundation 框架中的 NSNetService 和 NSNetServiceBrowser 类&#xff1b; CFNetServices&#xff0c;Core Services 中 CFNetwork 框架的一部分&#xff1b; Java 的 DN…

如何在现实场景中随心放置AR虚拟对象?

随着AR的发展和电子设备的普及&#xff0c;人们在生活中使用AR技术的门槛降低&#xff0c;比如对于不方便测量的物体使用AR测量&#xff0c;方便又准确&#xff1b;遇到陌生的路段使用AR导航&#xff0c;清楚又便捷&#xff1b;网购时拿不准的物品使用AR购物&#xff0c;体验更…

【每天进步一点点】函数表达式和函数声明

函数声明 function 函数名&#xff08;&#xff09;{} 函数声明会被率先读取。 函数声明后不会立即执行&#xff0c;会在我们需要的时候调用到。 由于函数声明不是一个可执行语句&#xff0c;所以不以分号结束。 函数表达式 表达式赋值给了一个变量 const 变量名 functi…

GT-suite v2016解决许可证过期问题(附新版liscense下载地址)

安装GT-suite v2016时遇到了如图报错的问题。当时的报错找不到了&#xff0c;下图是贴吧相同问题的报错图。 为了解决问题&#xff0c;先根据某网友的如下答复操作&#xff1a; 添加环境变量后仍然有相同报错。 看来需要寻找其他方法。 再尝试着卸载GT-suite v2016&#xff0c…

【Python--torch.nn.functional】F.normalize用法 + 代码说明

【Python–torch.nn.functional】F.normalize介绍 代码说明 文章目录【Python--torch.nn.functional】F.normalize介绍 代码说明1. 介绍2. 代码说明2.1 一维Tensor2.2 二维Tensor2.3 三维Tensor3. 总结1. 介绍 import torch.nn.functional as F F.normalize(input: Tensor, …