[External] : Re: ReversibleCollection proposal

Stuart Marks stuart.marks at oracle.com
Fri Apr 30 19:25:39 UTC 2021

OK, I think we can wrap up this portion of the thread.

As the proposal stands, it has add{First,Last} returning void instead of some useful 
value. For SortedSet and for LinkedHashMap's views, these throw UOE. Can we do better?

Collection has add(), Deque has add{First,Last} and offer{First,Last}, and 
BlockingDeque has put{First,Last}. I'm loathe to add new insertion methods that 
differ from these, either in signatures or semantics.

The BlockingDeque putX methods have blocking behavior, which only makes sense for a 
concurrent collection. Still, because these exist, we mustn't add putX methods 
elsewhere that have different semantics.

After having thought about this for a couple days, I think it's a really bad idea to 
reuse offerX methods. These would allow a collection to refuse the addition of an 
element and merely return a boolean indicating that. I can easily see people writing 
offerX code that doesn't check the return value, and if things shift around in their 
program, elements can be silently dropped. This would set a new precedent, as the 
present behavior is that collections can reject adding an element only by throwing 
an exception. Allowing refusal by returning 'false' would be a bad precedent.

So I think we're stuck with void-returning add{First,Last} methods.

Regarding throwing UOE, I think it's useful to distinguish between the LinkedHashMap 
views and SortedSet. Today, it's already the case that Map's view collections don't 
support addition, so the ReversibleX views of LinkedHashMap are similar in this regard.

SortedSet is different, as it's a top-level collection interface instead of a view 
collection. It's unusual for it not to support the addX operations. We explored some 
alternatives, such as throwing exceptions if preconditions aren't met. These seem 
fairly rare, and the alternative behaviors don't seem all that useful. In any case 
nothing emerged that was clearly better than simple UOE-throwing behavior.

OK, I think it's been useful to analyze various alternatives -- in particular I 
hadn't seriously considered the offerX methods -- but I'll leave the proposal as it 
stands regarding add{First,Last} methods.


On 4/28/21 6:54 AM, Peter Levart wrote:
> On 4/28/21 7:19 AM, Stuart Marks wrote:
>> On 4/27/21 9:17 AM, Anthony Vanelverdinghe wrote:
>>> On Tuesday, April 27, 2021 11:25 CEST, Peter Levart <peter.levart at gmail.com> wrote:
>>>> Now just some of my thoughts about the proposal:
>>>> - SortedSet.addFirst/.addLast - I think an operation that would be used
>>>> in situations like: "I know this element should always be greater than
>>>> any existing element in the set and I will push it to the end which
>>>> should also verify my assumption" is a perfectly valid operation. So
>>>> those methods throwing IllegalStateException in case the invariant can't
>>>> be kept seem perfectly fine to me.
>>> This was raised before and addressed by Stuart in [0]:
>>> "An alternative as you suggest might be that SortedSet::addFirst/addLast could throw
>>> something like IllegalStateException if the element is wrongly positioned.
>>> (Deque::addFirst/addLast will throw ISE if the addition would exceed a capacity
>>> restriction.) This seems error-prone, though, and it's easier to understand and
>>> specify that these methods simply throw UOE unconditionally. If there's a good use
>>> case for the alternative I'd be interested in hearing it though."
>> Yes, to be clear, it was Stephen Coleborne who raised this previously [1] and it's 
>> my response that's quoted above.
>> Some further thoughts on this.
>> This is an example where, depending on the current state of the collection, the 
>> method might throw or it might succeed. This is useful in concurrent collections 
>> (such as the capacity-restricted Deque above), where the caller cannot check 
>> preconditions beforehand, because they might be out of date by the time the 
>> operation is attempted. In such cases the caller might not want to block, but 
>> instead it might catch the exception and report an error to its caller (or drop 
>> the request). Thus, calling the exception-throwing method is appropriate.
>> Something like SortedSet::addLast seems different, though. The state is the 
>> *values* of the elements already in the collection. This is something that can 
>> easily be checked, and probably should be checked beforehand:
>>     if (sortedSet.isEmpty() || sortedSet.last().compareTo(e) <= 0)
>>         sortedSet.add(e);
>>     else
>>         // e wouldn't be the last element, do something different
> I was thinking more of a case where the else branch would actually throw 
> IllegalStateException and do nothing else - a kind of add with precondition check. A 
> precondition in the sense of Objects.requireNonNull(). I can't currently think of a 
> real usecase now, so this kind of operation is probably very rare. Probably not 
> useful since if you're adding to SortedSet, the order of elements added should not 
> matter because SortedSet will sort them. If you just want to check the order of 
> elements added and you are not willing to pay the price of SortedSet, you will be 
> adding them to a List or LinkedHashSet, but then the method would not do the check...
>> Now this is a fair bit of code, and it would be shorter just to call 
>> sortedSet.addLast(e). But does that actually help? What if e is already in the set 
>> and is the last element?
> In that case the operation would be a no-op (or it would replace the element with 
> the parameter - a slight difference as the element can be .equals() but not the same 
> instance).
>> Is catching an exception really what we want to do if e wouldn't be the last 
>> element? Maybe we'd want to do nothing instead. If so, catching an exception in 
>> order to do nothing is extra work.
> I was only thinking of situations where propagating the exception would be the 
> desired thing.
>> Again, I'd like to hear about use cases for a conditionally-throwing version of 
>> addLast et. al. I don't want to be limited by failure of imagination, but it does 
>> seem like this kind of behavior would be useful only in a narrow set of cases 
>> where it happens to do exactly the right thing. Otherwise, it just gets in the 
>> way, and its behavior is pretty obscure. So, color me skeptical.
> Right, I don't know how common such operation would be. Probably not very.
> Peter
> which are a continual source of errors.)
>> I'll think about this more, but it doesn't seem promising.
>> s'marks

More information about the core-libs-dev mailing list