Java 8 lambda 从列表中获取和删除元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35701337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 lambda get and remove element from list
提问by Marco Stramezzi
Given a list of elements, I want to get the element with a given property andremove it from the list. The best solution I found is:
鉴于元素的列表,我想用一个给定的属性来获取元素,并从列表中删除。我发现的最佳解决方案是:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Is it possible to combine get and remove in a lambda expression?
是否可以在 lambda 表达式中组合 get 和 remove?
回答by Tunaki
The direct solution would be to invoke ifPresent(consumer)
on the Optional returned by findFirst()
. This consumer will be invoked when the optional is not empty. The benefit also is that it won't throw an exception if the find operation returned an empty optional, like your current code would do; instead, nothing will happen.
直接的解决方案是调用ifPresent(consumer)
Optional 返回的 Optional findFirst()
。当可选项不为空时,将调用此使用者。好处还在于,如果 find 操作返回一个空的可选项,它不会抛出异常,就像您当前的代码那样;相反,什么都不会发生。
If you want to return the removed value, you can map
the Optional
to the result of calling remove
:
如果你想返回被移除的值,你可以map
在Optional
调用的结果remove
:
producersProcedureActive.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.map(p -> {
producersProcedureActive.remove(p);
return p;
});
But note that the remove(Object)
operation will again traverse the list to find the element to remove. If you have a list with random access, like an ArrayList
, it would be better to make a Stream over the indexes of the list and find the first index matching the predicate:
但请注意,该remove(Object)
操作将再次遍历列表以查找要删除的元素。如果你有一个随机访问的列表,比如 an ArrayList
,最好在列表的索引上创建一个 Stream 并找到与谓词匹配的第一个索引:
IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int) i));
With this solution, the remove(int)
operation operates directly on the index.
使用此解决方案,remove(int)
操作直接对索引进行操作。
回答by Bohemian
I'm sure this will be an unpopular answer, but it works...
我确信这将是一个不受欢迎的答案,但它有效......
ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0]
will either hold the found element or be null.
p[0]
将保存找到的元素或为空。
The "trick" here is circumventing the "effectively final" problem by using an arrayreference that is effectively final, but setting its first element.
这里的“技巧”是通过使用有效最终的数组引用来规避“有效最终”问题,但设置其第一个元素。
回答by Vasily Liaskovsky
Consider using vanilla java iterators to perform the task:
考虑使用 vanilla java 迭代器来执行任务:
public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
T value = null;
for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
if (test.test(value = it.next())) {
it.remove();
return value;
}
return null;
}
Advantages:
优点:
- It is plain and obvious.
- It traverses only once and only up to the matching element.
- You can do it on any
Iterable
even withoutstream()
support (at least those implementingremove()
on their iterator).
- 这是简单明了的。
- 它只遍历一次并且只遍历匹配元素。
Iterable
即使没有stream()
支持(至少是那些remove()
在他们的迭代器上实现的),你也可以做到。
Disadvantages:
缺点:
- You cannot do it in place as a single expression (auxiliary method or variable required)
- 您不能将其作为单个表达式就地执行(需要辅助方法或变量)
As for the
至于
Is it possible to combine get and remove in a lambda expression?
是否可以在 lambda 表达式中组合 get 和 remove?
other answers clearly show that it is possible, but you should be aware of
其他答案清楚地表明这是可能的,但你应该意识到
- Search and removal may traverse the list twice
ConcurrentModificationException
may be thrown when removing element from the list being iterated
- 搜索和删除可能会遍历列表两次
ConcurrentModificationException
从正在迭代的列表中删除元素时可能会抛出
回答by Donald Raab
With Eclipse Collectionsyou can use detectIndex
along with remove(int)
on any java.util.List.
使用Eclipse Collections,您可以在任何 java.util.List 上使用detectIndex
with remove(int)
。
List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
integers.remove(index);
}
Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
If you use the MutableList
type from Eclipse Collections, you can call the detectIndex
method directly on the list.
如果使用MutableList
Eclipse Collections 中的类型,则可以detectIndex
直接调用列表中的方法。
MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
integers.remove(index);
}
Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
Note: I am a committer for Eclipse Collections
注意:我是 Eclipse Collections 的提交者
回答by user140547
As others have suggested, this might be a use case for loops and iterables. In my opinion, this is the simplest approach. If you want to modify the list in-place, it cannot be considered "real" functional programming anyway. But you could use Collectors.partitioningBy()
in order to get a new list with elements which satisfy your condition, and a new list of those which don't. Of course with this approach, if you have multiple elements satisfying the condition, all of those will be in that list and not only the first.
正如其他人所建议的那样,这可能是循环和可迭代对象的用例。在我看来,这是最简单的方法。如果您想就地修改列表,无论如何都不能将其视为“真正的”函数式编程。但是您可以使用Collectors.partitioningBy()
来获取包含满足条件的元素的新列表,以及不满足条件的元素的新列表。当然,使用这种方法,如果您有多个满足条件的元素,所有这些都将在该列表中,而不仅仅是第一个。
回答by Marco Stramezzi
Combining my initial idea and your answers I reached what seems to be the solution to my own question:
结合我最初的想法和您的回答,我得出了似乎是我自己问题的解决方案:
public ProducerDTO findAndRemove(String pod) {
ProducerDTO p = null;
try {
p = IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int)i))
.get();
logger.debug(p);
} catch (NoSuchElementException e) {
logger.error("No producer found with POD [" + pod + "]");
}
return p;
}
It lets remove the object using remove(int)
that do not traverse again the
list (as suggested by @Tunaki) andit lets return the removed object to
the function caller.
它允许使用remove(int)
不再次遍历列表的对象删除对象(如@Tunaki 所建议的那样),并且允许将删除的对象返回给函数调用者。
I read your answers that suggest me to choose safe methods like ifPresent
instead of get
but I do not find a way to use them in this scenario.
我阅读了您的答案,这些答案建议我选择安全的方法,例如ifPresent
而不是,get
但我没有找到在这种情况下使用它们的方法。
Are there any important drawback in this kind of solution?
这种解决方案有什么重要的缺点吗?
Edit following @Holger advice
编辑以下@Holger 建议
This should be the function I needed
这应该是我需要的功能
public ProducerDTO findAndRemove(String pod) {
return IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int)i))
.orElseGet(() -> {
logger.error("No producer found with POD [" + pod + "]");
return null;
});
}
回答by uma shankar
To Remove element from the list
从列表中删除元素
objectA.removeIf(x -> conditions);
eg:
例如:
objectA.removeIf(x -> blockedWorkerIds.contains(x));
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
str1.removeIf(x -> str2.contains(x));
str1.forEach(System.out::println);
OUTPUT:A B C
输出:A B C
回答by asifsid88
Although the thread is quite old, still thought to provide solution - using Java8
.
虽然线程很旧,但仍然认为提供解决方案 - 使用Java8
.
Make the use of removeIf
function. Time complexity is O(n)
请使用removeIf
功能。时间复杂度为O(n)
producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
API reference: removeIf docs
API 参考:removeIf 文档
Assumption: producersProcedureActive
is a List
假设:producersProcedureActive
是一个List
NOTE: With this approach you won't be able to get the hold of the deleted item.
注意:使用这种方法,您将无法获得已删除的项目。
回答by Toi Nguyen
Use can use filter of Java 8, and create another list if you don't want to change the old list:
使用可以使用 Java 8 的过滤器,如果您不想更改旧列表,可以创建另一个列表:
List<ProducerDTO> result = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.collect(Collectors.toList());
回答by KimchiMan
The below logic is the solution without modifying the original list
下面的逻辑是不修改原始列表的解决方案
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
List<String> str3 = str1.stream()
.filter(item -> !str2.contains(item))
.collect(Collectors.toList());
str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]