java - Optional#map 可以将输入参数的状态更改为 Function lambda 吗?

标签 java java-8 monads option-type

我有一段Java代码,它从Optional#map的输入参数中包含的集合中删除一个元素

boolean ret = methodReturnsOptioanl()
                .map(project -> project.getDocIds().remove(docId))
                .orElse(false);

其中project.getDocIds()返回一组字符串ID并且保证不为空。

我已经测试过它并且可以工作;如果Optional为空或者集合中不存在docId,ret为false。

但是,Optional#map 可以执行此操作并更改成员集的状态并返回 Set#remove 操作的 boolean 结果吗?

我四处搜寻,找不到任何明确的答案。

最佳答案

我想说不,最好的方法是将您的 project 映射到分配给您的 project 对象的 docIds 并映射到然后调用终端操作Stream#orElse。此终端操作应构造一个新的(可变)列表/集合,然后您可以从中删除 docId

这样您的代码将如下所示:

boolean ret = optionalVal
              .map(Class::getDocIds)
              .orElse(new ArrayList<>())
              .remove(docId);

然而,更有效的内存解决方案是:

boolean ret = optionalVal
              .map(Class::getDocIds)
              .orElseGet(ArrayList::new)
              .remove(docId);

这与以下事实有关:仅当 optionalVal 变量为空时,才调用提供给 Optional#orElseGetSupplier。当您使用Optional#orElse时,将始终调用此方法,并且将构造一个空的(可能是不必要的)ArrayList并将其加载到堆中。这意味着当您的Optional不为空时,您将构造所需数量的两倍而不是一个对象。

说明

Stream#map 方法是一个中间操作,这意味着它将 Stream 转换为另一个流。不是这种情况。为此,您可以使用 orElse 操作作为终端操作,它会生成 List/Object 作为结果,以便您删除你的对象ID。

解释内存高效解决方案

Optional#orElseGet 仅在值存在时调用Supplier。运行以下测试来验证这一点:

public class TestTest {

    class TestOptional {

        public TestOptional(){
            System.out.println("TestOptional constructor called.. " + this);
        }

        List<String> getDocIds(){
            System.out.println("TestOptional#getDocIds called.. " + this);
            return new ArrayList<>(Collections.singletonList("test"));
        }

        List<String> getEmptyDocIds(){
            System.out.println("TestOptional#getEmptyDocIds called.. " + this);
            return new ArrayList<>();
        }
    }

    @Test(expected = Exception.class)
    public void test() throws Exception {

        Optional<TestOptional> optionalVal = Optional.of(new TestOptional());
        Optional<TestOptional> optionalValEmpty = Optional.empty();

        boolean deleted = optionalVal
                .map(TestOptional::getDocIds)
                .orElse(new TestOptional().getEmptyDocIds())
                .remove("test");

        System.out.println("One: " + deleted);

        System.out.println("\n ### \n");

        boolean deletedTwo = optionalVal
                .map(TestOptional::getDocIds)
                .orElseGet(() -> new TestOptional().getEmptyDocIds())
                .remove("test");

        System.out.println("Two: " + deletedTwo);

        System.out.println("\n ### \n");

        boolean deletedThree = optionalValEmpty
                .map(TestOptional::getDocIds)
                .orElse(new TestOptional().getEmptyDocIds())
                .remove("test");

        System.out.println("Three: " + deletedThree);

        System.out.println("\n ### \n");

        boolean deletedFour = optionalValEmpty
                .map(TestOptional::getDocIds)
                .orElseGet(() -> new TestOptional().getEmptyDocIds())
                .remove("test");

        System.out.println("Four: " + deletedFour);

        assertThat(deleted).isTrue();
        assertThat(deletedTwo).isTrue();
        assertThat(deletedThree).isFalse();
        assertThat(deletedFour).isFalse();
    }
}

测试输出:

TestOptional constructor called.. test.TestTest$TestOptional@28f67ac7
TestOptional#getDocIds called.. test.TestTest$TestOptional@28f67ac7
TestOptional constructor called.. test.TestTest$TestOptional@1a407d53
TestOptional#getEmptyDocIds called.. test.TestTest$TestOptional@1a407d53
One: true

 ### 

TestOptional#getDocIds called.. test.TestTest$TestOptional@28f67ac7
Two: true

 ### 

TestOptional constructor called.. test.TestTest$TestOptional@3cda1055
TestOptional#getEmptyDocIds called.. test.TestTest$TestOptional@3cda1055
Three: false

 ### 

TestOptional constructor called.. test.TestTest$TestOptional@79b4d0f
TestOptional#getEmptyDocIds called.. test.TestTest$TestOptional@79b4d0f
Four: false

但是:如果此代码使用时间较短且不那么频繁(如方法的使用量),则不会产生太大影响,因为此方法可能超出范围没时间。然而,垃圾收集器仍然需要做更多的工作,这意味着不必要地滥用存储字节。

关于java - Optional#map 可以将输入参数的状态更改为 Function lambda 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57591037/

相关文章:

java - IntelliJ 中的 JDBC mysql 驱动配置

java - 如何设置使用 Tomcat 的 servlet 可以处理的最大并发请求数

java - 错误 java.lang.ClassNotFoundException : com. fastxml.jackson.databind.Module

Java 8 Stream,获取头部和尾部

java - “java”命令有效,但 'javac' 无效

haskell - Control.MonadPlus.Free 没有不必要的分发

java - 如何自动刷新Listview?

java - 增强的 for 循环编译适用于 JDK 8 但不是 7

haskell - 状态单子(monad)的传播

haskell - 为什么我从书 "Functional Programming in Haskell"复制的haskell代码无法成功解释?