当前位置: 首页 > 知识库问答 >
问题:

Optional#map是否可以将输入参数的状态更改为函数lambda?

松高爽
2023-03-14

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

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

where项目。getDocId()返回一组字符串ID,并保证不为null。

我已经测试过它并有效;如果选项为空或docId不存在于集合中,ret为false。

但是,可选#map是否可以执行此操作并更改成员集的状态并返回Set#删除操作的布尔结果?

我到处找了找,找不到关于这件事的确切答案。

共有2个答案

龚彬
2023-03-14

这样使用map()可以吗?不,因为map()表达了将可选元素转换为新类型的意图,而这不是您要做的。

虽然没有要求map()操作没有副作用,并且您的原始代码会做您想要的事情,但它并没有做人们对map()的期望。未来的读者可能需要重新审视一下才能理解您的代码在做什么。我建议一些更明显的东西:

var project = methodReturnsOptional();
boolean ret = project.isPresent() && project.get().getDocIds().remove(docId);
裴钧
2023-03-14

我会说不,最好的方法是将您的项目映射到分配给您的项目Object的docIds,然后调用终端操作Stream#orElse。此终端操作应该构造一个新的(Mutable)List/Collection,然后您可以从中删除docId

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

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

但是,一个更节省内存的解决方案是:

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

这与这样一个事实有关,即只有当变量为空时,才会调用提供给可选#或lseget的供应商。当您使用可选的orElse时,将始终调用此方法,并且使用此方法将构造一个空的(可能是不必要的)ArrayList,并将其加载到堆中。这意味着,当可选对象不为空时,可以根据需要构造两倍的对象,而不是只构造一个。

解释

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

解释内存高效解决方案

当值不存在时,可选的OrelGet仅调用供应商。运行以下测试以验证这一点:

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

然而:如果此代码使用时间短,并且使用频率不高(如方法的使用量),这不会产生太大的影响,因为此方法可能很快就会超出范围。然而,垃圾收集器还有更多的工作要做,这意味着对存储字节的不必要滥用。

 类似资料:
  • 问题内容: 在Python中,是否可以在运行时重新定义函数的默认参数? 我在这里定义了带有3个参数的函数: 接下来,我尝试(未成功)设置y的默认参数值,然后尝试调用不带参数的函数: 但是由于未正确设置默认值,所以产生了以下错误: 正如我在此尝试的那样,是否可以在运行时重新定义函数的默认参数? 问题答案: 只需使用functools.partial 这里的一个问题:您将无法调用它,因为您应该手动说

  • 我想知道OpenCV是否能够将摄像头(dev/video1)设置为复合或S-video输入。 我使用的摄像头仅在复合输入中运行,但默认情况下,v4l2在S-Video输入中打开dev/video1。V4l2能够通过QT V4l2 utils应用程序从S视频转换为复合输入。 opencv正在使用v4l从相机捕获图像,我想在代码中使用OpenCV更改为复合输入。那有可能吗?如果不是,解决办法是什么?

  • 本文向大家介绍将数据写入块后是否可以更改数据?相关面试题,主要包含被问及将数据写入块后是否可以更改数据?时的应答技巧和注意事项,需要的朋友参考一下 回答:不,这是不可能的。如果需要进行任何修改,组织也必须从所有其他模块中删除信息。

  • 问题内容: 的 多重嵌套视图 功能非常好-您可以轻松地从应用程序的一种 状态 跳到另一种 状态 。 有时您可能想更改URL,但有时不需要。我觉得 状态 的概念应该与 route 分开/可选。 这是一个说明我意思的朋克。 这是文档中的一个小工具的分支,下面有2个小更改: 这似乎可行-URL保持不变。同样,这里做了多少多余的工作?这是经过批准/测试的用法吗? 如果您可以省略某个州的信息,那就太好了。

  • 可选类应该改变它持有的对象的状态吗?或者每个返回流的中间操作都不应该改变对象的可变性?示例: 当包含实例变量时,是否应用相同的原则。例如

  • 问题内容: 与在或中使用的方式类似: 问题答案: 是。 如果你不理会关键字参数,这很简单并且可以工作: 如你所见,Python将为你提供一个包含所有参数的元组。 对于关键字参数,你需要将其作为单独的实际参数接受,如的所示。