ในสตรีม Java8 ฉันได้รับอนุญาตให้แก้ไข / อัพเดตออบเจ็กต์ภายในหรือไม่ สำหรับเช่น List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
ในสตรีม Java8 ฉันได้รับอนุญาตให้แก้ไข / อัพเดตออบเจ็กต์ภายในหรือไม่ สำหรับเช่น List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
คำตอบ:
ได้คุณสามารถแก้ไขสถานะของออบเจ็กต์ภายในสตรีมของคุณได้ แต่ส่วนใหญ่แล้วคุณควรหลีกเลี่ยงการแก้ไขสถานะของแหล่งที่มาของสตรีม จากส่วนการไม่รบกวนของเอกสารชุดสตรีมเราสามารถอ่านได้ว่า:
สำหรับแหล่งข้อมูลส่วนใหญ่การป้องกันการรบกวนหมายถึงการตรวจสอบว่าแหล่งข้อมูลไม่ได้ถูกแก้ไขเลยในระหว่างการดำเนินการของไปป์ไลน์สตรีม ข้อยกเว้นที่น่าสังเกตสำหรับสิ่งนี้คือสตรีมที่มีแหล่งที่มาเป็นคอลเล็กชันพร้อมกันซึ่งได้รับการออกแบบมาโดยเฉพาะเพื่อจัดการการแก้ไขพร้อมกัน แหล่งที่มาของสตรีมพร้อมกันคือแหล่งที่
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(int i=0; ..; ..)
ซึ่งไม่มีปัญหานี้ (แต่จะไม่หยุดคุณเมื่อเธรดอื่นจะแก้ไขรายการของคุณ) นอกจากนี้คุณยังสามารถใช้วิธีการเช่น,list.removeAll(Collection)
list.removeIf(Predicate)
นอกจากนี้ยังมีระดับมีไม่กี่วิธีซึ่งอาจจะเป็นประโยชน์เช่นjava.util.Collections
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]
}
}
ในโซลูชันนี้รายการต้นฉบับจะไม่ถูกแก้ไข แต่ควรมีผลลัพธ์ที่คุณคาดหวังไว้ในรายการใหม่ที่สามารถเข้าถึงได้ภายใต้ตัวแปรเดียวกับรายการเก่า
ในการปรับเปลี่ยนโครงสร้างของแหล่งที่มาของสตรีมดังที่ Pshemo กล่าวไว้ในคำตอบของเขาทางออกหนึ่งคือสร้างอินสแตนซ์ใหม่ที่Collection
คล้ายArrayList
กับรายการในรายการหลักของคุณ วนซ้ำในรายการใหม่และดำเนินการกับรายการหลัก
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
ในการกำจัดออกจากConcurrentModificationException ให้ใช้CopyOnWriteArrayList
List<Something>
- คุณจะไม่รู้เลยว่านั่นคือ CopyOnWriteArrayList หรือไม่ ดังนั้นนี่จึงเป็นเหมือนความคิดเห็นธรรมดา ๆ แต่ไม่ใช่คำตอบที่แท้จริง
แทนที่จะสร้างสิ่งแปลก ๆ คุณสามารถทำได้filter()
แล้วmap()
ผลลัพธ์ของคุณ
อ่านได้มากขึ้นและแน่นอน สตรีมจะทำให้เป็นเพียงลูปเดียว
คุณสามารถใช้removeIf
เพื่อลบข้อมูลออกจากรายการตามเงื่อนไข
เช่น: - หากคุณต้องการลบเลขคู่ทั้งหมดออกจากรายการคุณสามารถทำได้ดังนี้
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
ได้คุณสามารถแก้ไขหรืออัปเดตค่าของวัตถุในรายการในกรณีของคุณได้เช่นเดียวกัน:
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 ไม่อนุญาตให้แก้ไขตัวชี้เองและด้วยเหตุนี้หากคุณประกาศเพียงแค่นับเป็นตัวแปรและพยายามเพิ่มขึ้นภายในสตรีมมันจะไม่ทำงานและทำให้เกิดข้อยกเว้นของคอมไพเลอร์ตั้งแต่แรก
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 + "]";
}
}