我有一段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#orElseGet
的 Supplier
。当您使用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/