Copy-on-Write Array in JavaScript
Copy-on-write is a powerful optimization technique where data is shared until a modification is attempted. This challenge asks you to implement a copy-on-write array in JavaScript. This pattern is useful for scenarios where you have multiple references to an array, and you want to avoid unnecessary copying until a modification is actually needed, saving memory and potentially improving performance.
Problem Description
You are to create a CopyOnWriteArray class in JavaScript. This class should mimic the behavior of a standard JavaScript array, but with the copy-on-write optimization. The core idea is that multiple CopyOnWriteArray instances can initially point to the same underlying array data. Only when one instance attempts to modify the array does it create a new copy of the data, leaving other instances unaffected.
Key Requirements:
- Constructor: The constructor should accept an initial array as an argument.
- Read-Only Methods: Methods like
length,at(index),slice(),map(),filter(),reduce(),forEach()should return the same values as a standard JavaScript array, without creating copies. These methods should operate on the shared underlying array. - Write-Only Methods: Methods like
push(),pop(),shift(),unshift(),splice(),set(index, value), and direct assignment to elements (e.g.,array[index] = value) should trigger a copy of the underlying array before performing the modification. After the copy, the modification should be applied to the copy, and theCopyOnWriteArrayinstance should then manage this new copy. Other instances should continue to use the original shared array. - Immutability of Original: Modifying one
CopyOnWriteArrayinstance should never affect other instances that share the same underlying array. - Chaining: Methods that return a new
CopyOnWriteArray(e.g.,map(),filter()) should return a new instance ofCopyOnWriteArray, not a standard JavaScript array.
Expected Behavior:
- Multiple
CopyOnWriteArrayinstances created from the same initial array should initially share the same data. - Modifying one instance should create a copy and only affect that instance.
- Read operations on any instance should return the correct values, reflecting the state of the underlying array at the time of the read.
- Methods that return arrays (like
slice(),map(),filter()) should return newCopyOnWriteArrayinstances.
Edge Cases to Consider:
- Empty initial array.
- Modifying elements at the beginning, middle, and end of the array.
- Chaining multiple write operations (e.g.,
map().filter().push()). set()method with out-of-bounds indices (should throw an error, like standard arrays).splice()with negative indices.
Examples
Example 1:
Input:
const arr = [1, 2, 3];
const copy1 = new CopyOnWriteArray(arr);
const copy2 = new CopyOnWriteArray(arr);
console.log(copy1.length); // Output: 3
console.log(copy2.length); // Output: 3
console.log(copy1[0] === copy2[0]); // Output: true
copy1[0] = 10;
console.log(copy1.length); // Output: 3
console.log(copy2.length); // Output: 3
console.log(copy1[0]); // Output: 10
console.log(copy2[0]); // Output: 1
Explanation: Initially, copy1 and copy2 share the same array. Modifying copy1 triggers a copy, so copy2 remains unchanged.
Example 2:
Input:
const arr = [1, 2, 3];
const copy1 = new CopyOnWriteArray(arr);
const copy2 = copy1.map(x => x * 2);
console.log(copy1[0]); // Output: 1
console.log(copy2[0]); // Output: 2
copy1[0] = 10;
console.log(copy1[0]); // Output: 10
console.log(copy2[0]); // Output: 2
Explanation: map() creates a new CopyOnWriteArray. Modifying copy1 after the map() operation doesn't affect copy2.
Example 3: (Edge Case)
Input:
const arr = [];
const copy1 = new CopyOnWriteArray(arr);
copy1.push(1);
console.log(copy1.length); // Output: 1
console.log(copy1[0]); // Output: 1
Explanation: Handles the case of an empty initial array correctly.
Constraints
- The initial array can contain any valid JavaScript data types.
- The array length will not exceed 1000.
- Performance: While not a primary focus, avoid unnecessary copying. The copy should only happen when a write operation is performed.
- The
set()method should throw aRangeErrorif the index is out of bounds, mirroring standard JavaScript array behavior.
Notes
- Consider using a private property to store the underlying array.
- Think carefully about when and how to create copies.
- The
map()andfilter()methods should return newCopyOnWriteArrayinstances, not standard JavaScript arrays. This is crucial for maintaining the copy-on-write behavior. - Pay close attention to how write operations affect other instances of the
CopyOnWriteArray.