[External] : Re: Sequenced Collections

Stuart Marks stuart.marks at oracle.com
Tue Oct 11 00:54:21 UTC 2022


It sounds like you're after some generalized notion of "consistency", and the fact 
that offer*() return a boolean whereas add*() do not seems inconsistent. 
Unfortunately, the methods have different semantics. After add(obj), obj is *always* 
a member of the collection, whereas after offer*(obj), obj *might or might not* be a 
member of the collection. The semantics of addFirst/addLast are more closely aligned 
with those of the add*() methods on Collection, so I decided to promote 
addFirst/addLast to SequencedCollection instead of offerFirst/offerLast.

On your other point, if you want to compare elements of a SequencedCollection to 
those of another, in encounter order, you can just use regular Iterators for that. 
ListIterator isn't necessary.

s'marks

On 10/5/22 3:36 PM, Ernie Rael wrote:
> On 10/5/22 9:34 AM, Stuart Marks wrote:
>>
>>
>> On 10/4/22 9:38 PM, Ernie Rael wrote:
>>> Summary of key points (maybe the mail was TL;DR)
>>
>> OK thanks, I was still mulling over the previous email wondering which parts were 
>> significant enough to reply to.
>>
>>>   * SequencedCollection methods addFirst,addLast are the only methods in
>>>     Collection hierarchy (AFAIK) that might modify the collection and do
>>>     not return/signal if the collection was modified. Seems like
>>>     offerFirst,offerLast are more consistent with Collections and still
>>>     avoid method proliferation.
>>
>> The problem is that the boolean return values from add() and from offerX() mean 
>> different things, and having them be adjacent on List would be confusing. (Yes, 
>> they're both present on Deque, which is one of the reasons Deque is too 
>> complicated.) And we couldn't adjust the semantics of SequencedCollection.offerX() 
>> to match add(), as that would clash with the existing semantics on Deque.
> 
> 
> It is not uncommon for a sub-interface/sub-class to clarify the precise meaning of a 
> method. If there's a general definition of SequencedCollection.offerFirst(e), then 
> the Deque definition clarifies the meaning in that context. Consider:
> 
> Collections.add(e) - Returns true if this collection changed as a result of the call
> List.add(e) - Return true
> Set.add(e) - Returns true if this set did not already contain the specified element
> 
> 
>>
>> From your other messages it seems like you want the boolean return value in order 
>> to keep track of whether the collection changed such that it affects equals() and 
>> hashCode(). 
> 
> 
> No, I was just discussing the relationship of change and equals() when working with 
> a SequencedCollection; it's more observations around using SeqCol. It's interesting 
> that an addAll() can permute the structure, and end up at the same place.
> 
> 
>> There are other methods that might modify collections where you can't tell whether 
>> they actually modified the collection; consider Collection::clear or 
>> List::replaceAll. 
> 
> 
> I'll be more precise: methods that work with a single item return/signal change; 
> most bulk operators such as removeif(), retainAll(), removeAll(), addAll() also 
> return/signal change.
> 
> My main point is that "void SequencedCollection.addFirst(e)" is inconsistent with 
> Collections' design. clear() is, well, clear(). replaceAll() seems to be an 
> exception (a lone exception?).
> 
> 
>> So I don't think the boolean return value from add/offer is really buying you all 
>> that much.
> 
> 
> When I put together a class based on a Collection, I like to follow the general 
> design pattern. Not sure if/when I may have used the "return change" when using a 
> collection. But when sub-classing a collection, since everything does it, so do I; 
> I'll return change in any additional methods I might add. Consistent, least surprise...
> 
> 
>>
>>>   * With LinkedHashSet, seems like listIterator is missing. Rather than
>>>     making that part of SequencedCollection, maybe an "interface
>>>     ListIterable { ListIterator listIterator(int index); }". In addition
>>>     to modification, bi-directional might also be optional, to support
>>>     it's use with list equals.
>>
>> ListIterator has indexes and so it's pretty much tied to List. Maybe what you're 
>> missing from LinkedHashSet 
> 
> 
> I want to be able to do List.equals(SequencedCollection) and vice versa (in 
> particular with LinkedHashSet). In the list equals() implementations I've looked at, 
> they all use two ListIterator to do the compare; only forward iteration.  For 
> equals(), I think can wrap the SequencedCollection iterator in a forward, 
> uni-directional listIterator, a little messy, and use that for equals(); support 
> from the infrastructure would be nice. Which is where the idea of "ListIterator 
> Collections.listIterator(iterator, index)" in the other email comes from.
> 
> Some daydreaming: For equals(), indexes don't matter except for initialization. And 
> as far as "index ... tied to list", if SequencedCollection had a listIterator, I 
> think I could form sub-sequence from that, with only forward iteration. But 
> sub-SequencedCollection is a different topic. My usage requirement now is for equals().
> 
> Lists may include index, but they don't really depend on it unless they're 
> RandomAccess; consider LinkedList. I don't see how indexes have a bearing on this 
> discussion; in a listIterator the index is contained/maintained in the iterator.
> 
> 
>> is the ability to insert or reposition an element somewhere in the middle? This is 
>> certainly a valid use case, but it's quite rare, and I'm not sure I'd want to 
>> support it using an Iterator style mechanism anyway.
>>
>> Surveying the usages of ListIterator, 
> 
> 
> The implicit use by equals() is missing.
> 
> 
>> I found that it's mainly used for iteration in reverse, and secondarily for 
>> replacing all the elements of a List (somewhat supplanted by replaceAll). We did 
>> run into one case where ListIterator was used to edit items within a list but it 
>> turned out to be INCREDIBLY clumsy to use. So if we want to support that (fairly 
>> rare) use case, I wouldn't start with a ListIterator or some variant with indexes 
>> removed. I'd also want to see use cases before considering adding new mechanisms.
>>
>> s'marks
>>
> 


More information about the core-libs-dev mailing list