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