Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnmodifiableMultiset.removeIf() should throw UnsupportedOperationException #6702

Closed
etellman opened this issue Sep 5, 2023 · 7 comments
Closed
Assignees
Labels
P4 no SLO

Comments

@etellman
Copy link
Contributor

etellman commented Sep 5, 2023

UnmodifiableMultiset.removeIf() should throw UnsupportedOperationException, even if nothing would be removed. This would make it match the other mutating methods in UnmodifiableMultiset.

Currently, if the predicate doesn't accept any of the elements in the set, removeIf succeeds and does nothing. The set isn't modified, but it's surprising that removeIf doesn't throw UnsupportedOperationException in this case.

I'll attach a PR which will fix this, assuming you agree it should be changed.

etellman added a commit to etellman/guava that referenced this issue Sep 5, 2023
Even if there's nothing to remove, removeIf() should throw an
`UnsupportedOperationException`.

see google#6702
@perceptron8
Copy link
Contributor

IMO it's somewhat similar to #3909.

@etellman
Copy link
Contributor Author

etellman commented Sep 5, 2023

Thanks for the very quick response. You're right, it is the same idea as #3909. As @kevinb9n said in that one:

With that said, I do think that the behavior you're asking for is slightly better. It's better to always fail on a "structurally" invalid request even though it "just happened to be harmless" in these particular circumstances. Not doing so can create a "time bomb" in the code.

That was my thought for this one as well.

@etellman
Copy link
Contributor Author

etellman commented Sep 5, 2023

Here's a simple PR to fix it, if desired: #6703

@eamonnmcmanus
Copy link
Member

The downside, of course, is that this may break code that was implicitly relying on the current behaviour. Even if it might have been better to throw from the outset, it's less clear that changing it now is worth the small risk of breakage.

@etellman
Copy link
Contributor Author

etellman commented Sep 6, 2023

Makes sense.

For context, this came up as part of adding an isImmutable() assertion to AssertJ. If a collection throws UnsupportedOperationException for all potentially mutating operations, it's considered immutable. This works for any of the Guava collections that extend ImmutableCollection and almost works for UnmodifiableMultiset, except for removeIf() which always succeeds when the set is empty.

AssertJ also keeps a list of known immutable collection types, to handle cases like this. If you decide to keep the current behavior, we can add UnmodifiableMultiset to this list, since it actually is unmodifiable.

@eamonnmcmanus
Copy link
Member

I tried running all of Google's tests (from nearly all of Google's products and services) with an equivalent change in place, and there were no failures. There are actually only a few dozen uses of Multisets.unmodifiableMultiset in our millions of source files. So I think it would actually be OK to make this change. In the very unlikely event that anyone is broken by it, they were already skating on the edge of brokenness anyway.

@etellman
Copy link
Contributor Author

Sounds good. Thanks!

copybara-service bot pushed a commit that referenced this issue Sep 12, 2023
Even if there's nothing to remove, `removeIf(Predicate)` should throw an
`UnsupportedOperationException`. This is theoretically an incompatible change, in that some existing code might have been calling `removeIf` with a predicate that is never true. None of Google's internal uses show this problem in tests.

Fixes #6702.
Closes #6703.

RELNOTES=`Multisets.unmodifiableMultiset(set).removeIf(predicate)` now throws an exception always, even if nothing matches `predicate`.
PiperOrigin-RevId: 564194620
copybara-service bot pushed a commit that referenced this issue Sep 12, 2023
Even if there's nothing to remove, `removeIf(Predicate)` should throw an
`UnsupportedOperationException`. This is theoretically an incompatible change, in that some existing code might have been calling `removeIf` with a predicate that is never true. None of Google's internal uses show this problem in tests.

Fixes #6702.
Closes #6703.

RELNOTES=`Multisets.unmodifiableMultiset(set).removeIf(predicate)` now throws an exception always, even if nothing matches `predicate`.
PiperOrigin-RevId: 564194620
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P4 no SLO
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants