在 Android API < 24 上使用 Java 8 Stream API 是否可行?

99

我已经阅读了这篇帖子。但是,我仍然无法在minSdkVersion < 24上运行包含Java 8 Stream API功能的代码,例如以下代码。

List<Car> newCars = cars.stream()
                        .filter(s -> s.getColor().equals("red"))
                        .collect(Collectors.toList());

由于错误信息,此代码无法运行。

调用需要API级别24(当前最小值为15):java.util.Collection#stream

那么有人知道解决方案吗?


4
不可以在API 24以下使用Java Stream。但是有第三方库也可以实现同样的功能。 - tyczj
1
我有一个小型库,可以为较低的API执行类似的操作:https://github.com/smaspe/FunctionalIterables - njzk2
2
请参考以下类似的问题(和答案):https://dev59.com/DJrga4cB1Zd3GeqPl2K6 和 http://stackoverflow.com/questions/39039566/is-it-possible-to-use-java-8-features-optional-and-stream-for-android-14-and-hig。 - Sartorius
3
你可以使用新的Jack编译器,针对Gingerbread及以上版本的Android平台,使用lambda表达式和方法引用。但是在SDK 24以下的版本中,你无法使用默认/静态接口方法和Java 8特定的API,如Stream API。 - Sartorius
1
您可以在API 24下使用StreamSupport类。 - Özer Özcan
6个回答

87
您不能在API级别低于24的情况下使用Java8流技术。但是,有一些库可以支持部分流功能,这包括:Lightweight-Stream-APIsolidstreamsupport(由@sartorius在评论中提到)。更新:现在可以在不需要最低API级别的情况下使用Java 8 Stream API及更多功能。详情请参见此处。还可以查看android-retrostreams,它是从streamsupport派生出来的一个项目,利用了Android Studio 3.x D8 / desugar工具链跨Jar文件边界使用接口默认和静态方法的能力。此外还有其他android-retroXXX,例如CompletableFuture。

9
尽管 "solid" 和 "Lightweight-Stream-API" 都是流式编程的有价值选项,但在我看来,它们两者都不能被视为 Java 8 Stream API 的“后移”版本。我所知道的唯一一个可以声称在语法和功能上与 Java 8 流相当甚至接近的项目是 "streamsupport"。例如,它也支持并行流。虽然这对于 Android 开发人员可能不太重要。 - Sartorius
在我看来,Lightweight-Stream-API 在 Android 上最好用,因为它支持 Gradle,并且还添加了像“select”、“groupBy”等额外的操作。具体可以参考 https://github.com/aNNiMON/Lightweight-Stream-API#additional-operators。 - LordParsley
2
@LordParsley,streamsupport也支持Gradle。至于额外的内容,没错,这是真的。据我所知,扩展/偏离官方Java 8 API从未是streamsupport的设计目标。 - Sartorius
3
@Sartorius是正确的-我建议查看Github镜像以获取用法细节和Gradle参考https://github.com/streamsupport/streamsupport - LordParsley
我已经阅读过一般情况下Java Streams比集合慢的说法。 https://dev59.com/vmEh5IYBdhLWcg3wNxPM 那这些回溯呢? - Shikhar Shrivastav

17

没错!从 AGP 4.0.0 开始,我们可以使用 Stream API 和其他一些 Java 8+ APIs(例如java.time)。

您的应用程序的 build.gradle 最低所需更改:

android {
  compileOptions {
    // Flag to enable support for the new language APIs
    coreLibraryDesugaringEnabled true
  }
}

dependencies {
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
}

15
DexGuard 8.2版本发布以来,Android设备<API 24也可以使用Java 8 streams API。为此,需要包含streamsupport库,DexGuard将把所有Java 8 stream API调用转换为提供的库。不需要额外处理,开发人员可以直接使用提供的Java 8 streams API进行编码。依赖项也会自动转换,因此可以在Android开发中使用具有Java 8功能的库。
这个功能将很快包括在ProGuard中,敬请关注。
编辑:已经存在beta版本的Proguard 6.1.0支持回溯Java 8 stream和time API。

这不是官方库。 - user25
我对这个库本身没有真正的了解,但我建议在商业项目中使用之前检查库的许可证。GNU通用公共许可证第2版,带有类路径例外. 可能需要确认一下。 - Michal Harakal
5
@MichalHarakal花了我几秒钟才意识到layer实际上是lawyer :) - superjos
我们的应用程序在发布版本中由于Proguard而在解糖过程中崩溃,有什么办法可以修复吗?这是SO问题https://stackoverflow.com/q/67656105/12204620 - Bitwise DEVS

6

可能有点晚了,但我刚看到如果我们使用java8.util.stream.StreamSupport,那么我们可以在Api 24之前运行相同的函数式编程方法。例如来自web3j -

StreamSupport.stream(transactionReceipt.getLogs())
            .map(log -> extractEventParameters(event, log))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());

3
@AjahnCharles 告诉别人在涉及语言 A 的问题上只使用语言 B 不是一个好的答案。许多项目仍然使用 Java 编写。 - mkuech
2
@AjahnCharles,您提出了一个很好的观点,即引起读者对_可能_考虑更改Android生态系统的注意。 话说回来,问题是关于Java的一个特定功能,它仍然是Android开发的官方支持语言。 即使“只是”混合化您的应用程序,切换也会带来成本(学习曲线,也许他们的团队不支持它等),这需要深入探讨比问题所要求的要多得多的内容。 更重要的是,该评论没有解释Kotlin是如何解决问题的,这使其更脱离上下文,也不太有帮助。 - mkuech
3
当明确提到“Java 8 Stream API”时,我认为答案也应该用JAVA写出来! - Abhriya Roy

1

我喜欢使用的另一种解决方案是在Java代码中静态使用Kotlin的扩展集合,如果项目当然支持

List<Car> newCars = CollectionsKt.filter(cars, s -> s.getColor().equals("red"));

同样适用于.map.reduce.first等等


-6
创建一个接口。
public interface Pre<T,R> {
            R get(T item);
        }

创建一个筛选方法。
public static  <T> List<T> Filter(List<T> list, Pre<T,Boolean> pre) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
        {
            return list.stream().filter(p -> pre.get(p)).collect(Collectors.toList());
        }
        else
        {
            List<T> col = new ArrayList<T>();
            for (int i = 0 ; i < list.size() ; i++)
                if (pre.get(list.get(i)))
                    col.add(list.get(i));
            return col;
        }
    }

使用代码

    public static class model {
            public String Name;
        }
List<model> models = new ArrayList<>();
            ...
            List<model> filtermodels = Filter(models,p-> p.Name.equals("filter"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接