Lists.transform: Preserve spliterator characteristics from backing list#8170
Conversation
|
I will admit my primary concern is that it is not obvious to me that we can correctly guarantee the semantics of these properties. Are they preserved e.g. by |
|
Yes, This makes sense because both characteristics describe the source, not the elements:
A mapping function transforms elements but doesn't change whether the underlying source is immutable or concurrent-safe. That's different from Guava's own |
|
I may have done the testing wrong, but my testing indicated that both I do agree that there's an argument for keeping both; I just don't think we can argue that on the grounds of matching JDK behavior. |
|
You're right, I just tested it and Though worth noting this PR doesn't change that behavior. |
Summary
spliterator()inTransformingSequentialListandTransformingRandomAccessListto delegate to the backing list's spliterator viaCollectSpliterators.map()IMMUTABLEandCONCURRENTfrom the backing listIMMUTABLEcharacteristic preservation withCopyOnWriteArrayListMotivation
When using
Lists.transform()with aCopyOnWriteArrayList, concurrent modifications cause spuriousConcurrentModificationException:Root cause:
CopyOnWriteArrayList.spliterator()has theIMMUTABLEcharacteristic, which tells the Stream API it's safe to iterate without CME concerns. However,Lists.transform()returns a list that inherits fromAbstractList, whose defaultspliterator()usesRandomAccessSpliterator- this does NOT preserve theIMMUTABLEcharacteristic, causing index-based iteration that throwsIndexOutOfBoundsException(converted to CME) when the list is modified.The fix: Override
spliterator()to useCollectSpliterators.map(), which preservesIMMUTABLE,CONCURRENT,ORDERED,SIZED, andSUBSIZEDcharacteristics from the backing spliterator. This matches the pattern already used inCollections2.TransformedCollection.Testing
ListsTesttests passtestTransformSpliteratorPreservesCharacteristics()verifiesIMMUTABLEis preservedCompatibility
This change only affects the spliterator behavior, not iteration via
Iterator. The spliterator will now correctly report characteristics inherited from the backing list, which is strictly more correct behavior.Fixes #8165
RELNOTES=
Lists.transform()now preserves spliterator characteristics (likeIMMUTABLE) from the backing list, preventing spuriousConcurrentModificationExceptionwhen wrapping concurrent lists.