在Java8流中,是否允许在其中修改/更新对象?例如。List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
在Java8流中,是否允许在其中修改/更新对象?例如。List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Answers:
是的,您可以修改流内部对象的状态,但是大多数情况下应避免修改流源的状态。从流软件包文档的“无干扰”部分,我们可以了解到:
对于大多数数据源,防止干扰意味着确保在流管道执行期间根本不修改数据源。值得注意的例外是流的源是并发集合,这些流是专门为处理并发修改而设计的。并发流源是那些
Spliterator
报告其CONCURRENT
特征的流源。
这样就可以了
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
但这在大多数情况下不是
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
并可能引发ConcurrentModificationException
甚至其他意外异常,例如NPE:
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
list.stream()
.filter(i -> i > 5)
.forEach(i -> list.remove(i)); //throws NullPointerException
Iterator
这样做,以免在迭代时修改列表(删除/添加新元素),并且流和for-each循环都在使用它。您可以尝试使用for(int i=0; ..; ..)
不存在此问题的简单循环(但当其他线程修改您的列表时,它不会阻止您)。你也可以使用类似的方法list.removeAll(Collection)
,list.removeIf(Predicate)
。而且java.util.Collections
class很少有像有用的方法addAll(CollectionOfNewElements,list)
。
filter()
功能性方法应为:
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTestRun {
public static void main(String[] args) {
List<String> lines = Arrays.asList("a", "b", "c");
System.out.println(lines); // [a, b, c]
Predicate<? super String> predicate = value -> "b".equals(value);
lines = lines.stream().filter(predicate.negate()).collect(toList());
System.out.println(lines); // [a, c]
}
}
在此解决方案中,原始列表不会被修改,但应将您的预期结果包含在新列表中,该列表可在与旧列表相同的变量下访问
而不是创造奇怪的事情的,你可以filter()
,然后map()
你的结果。
这更具可读性和确定性。流将仅使它循环一次。
是的,您同样可以修改或更新列表中对象的值:
users.stream().forEach(u -> u.setProperty("some_value"))
但是,以上语句将对源对象进行更新。在大多数情况下,这可能是不可接受的。
幸运的是,我们确实有另一种方式:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
它将返回更新的列表,而不会妨碍旧列表。
如前所述-您无法修改原始列表,但可以流式传输,修改并将项目收集到新列表中。这是如何修改字符串元素的简单示例。
public class StreamTest {
@Test
public void replaceInsideStream() {
List<String> list = Arrays.asList("test1", "test2_attr", "test3");
List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
System.out.println("Output: " + output); // Output: [test1, test2, test3]
}
}
这可能会晚一点。但这是用法之一。这可以获取文件数量的计数。
创建一个指向内存的指针(在这种情况下为新的obj),并修改对象的属性。Java 8流不允许修改指针本身,因此,如果您声明只将count视为变量并尝试在流中递增,则它将永远无法工作并首先抛出编译器异常
Path path = Paths.get("/Users/XXXX/static/test.txt");
Count c = new Count();
c.setCount(0);
Files.lines(path).forEach(item -> {
c.setCount(c.getCount()+1);
System.out.println(item);});
System.out.println("line count,"+c);
public static class Count{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Count [count=" + count + "]";
}
}