Factory methods for SequencedSet and SequencedMap

Joseph D. Darcy joe.darcy at oracle.com
Sat Jan 18 01:14:32 UTC 2025


On 1/17/2025 5:00 PM, David Alayachew wrote:
>
> Thanks for the corrections folks. I was thinking from the perspective 
> of LSP. I now see that there is the performance perspective to 
> consider too.
>
> Now that said, I don't understand your comment Joe Darcy. Could you 
> explain it in more detail?
>
Say you compile your code against JDK 24 and use the 1-argument Set.Of() 
method. For that call site, your class file will refer to a method using 
information akin to

     "In the class java.util.Set, a method named "of" that *returns a 
java.util.Set* and take a java.lang.Object as an argument"

(The generic information is basically erased in the class file, hence 
Set rather than Set<E> and Object rather than E.)

If we were then in JDK 25 to replace in java.util.Set

     static <E> Set<E> of(E e1){...}

with

     static <E> SequencedSet<E> of(E e1){...}

when your class file ran against JDK 25, there would be no method

     "In the class java.util.Set, a method named "of" that *returns a 
java.util.Set* and take a java.lang.Object as an argument"

for your class to call and the linkage would fail.

For static methods, the change is equivalent to removing a method and 
adding back a different, same-named method.

HTH,

-Joe


> My initial pick up of your comment is that, the parameter types and 
> the return types of a method must match the types exactly between 
> releases, otherwise there are no bridge methods FOR STATIC TYPES. But 
> as for why, I don't understand.
>
> I know that static methods are not so much inherited as they are just 
> given as is (hence why there is not really a static abstract method). 
> But I don't quite see the line connecting that with no bridge methods 
> for static. Maybe I don't understand bridge methods well enough.
>
>
> On Fri, Jan 17, 2025, 12:32 PM Joseph D. Darcy <joe.darcy at oracle.com> 
> wrote:
>
>     On 1/16/2025 11:26 PM, Rafael Winterhalter wrote:
>>     Would it even be possible to change the return types of
>>     Set.of(...) and Map.of(...) without breaking binary compatibility?
>
>
>     In short, no.
>
>     The methods in question are *static* methods. Switching to
>     covariant overrides with more precise return types works for
>     subclasses because of bridge methods.
>
>     In a bit more detail, in a covariant override a single method in
>     the source code gets translated into multiply methods in the class
>     file. References to methods in the class file use the argument
>     types and return type so if an old class file refers to the
>     previously declared source-level return type, there is the bridge
>     method present to be linked to (for binary compatibility) and then
>     executed.
>
>     -Joe
>
>
>>
>>     I also think that the randomization of Set.of(...) and
>>     Map.of(...) is a good property as it uncovers bugs early if one
>>     relies on iteration order. This especially since those methods
>>     are often used in tests where production code would use a proper
>>     HashSet which cannot guarantee iteration order for good reasons.
>>     Exactly here I think the new interfaces are a good addition as it
>>     uncovers such misconceptions. If code relies on insertion order,
>>     providing a Set.of(...) does no longer compile, what is a good thing.
>>
>>     To me, adding SequencedSet.of(...) and SequencedMap.of(...)
>>     sounds like the right approach, with implementations similar to
>>     that of Set.of(...) and Map.of(...). As for megamorphism, I think
>>     the chance of encounter at a call site is similar, as Set12 and
>>     SetN from the Set interface are typically combined with HashMap.
>>     As for a possible SequencedSet12 and SequencedSetN, I think they
>>     would normally be seen with LinkedHashSet.
>>
>>     Best regards, Rafael
>>
>>     Am Fr., 17. Jan. 2025 um 00:36 Uhr schrieb David Alayachew
>>     <davidalayachew at gmail.com>:
>>
>>         I should also add, the documentation went out of their way to
>>         specify that iteration order is unspecified.
>>
>>         Also, I see Rémi's comment, but that's even more unconvincing
>>         to me.
>>
>>         Map.of has an upper limit of 10 entries, and Map.ofEntries
>>         has an upper limit of that Java max file size limit thing.
>>         You all know what I am talking about.
>>
>>         Point is, both of these static factories were meant to be
>>         used on a small number of entries. If it truly has just been
>>         not done until now, then the bug database will confirm that
>>         easily.
>>
>>         When I get back, I can check myself.
>>
>>
>>         On Thu, Jan 16, 2025, 6:25 PM David Alayachew
>>         <davidalayachew at gmail.com> wrote:
>>
>>             I guess let me ask the obvious question.
>>
>>             Chesterton's fence -- why wasn't this done before? I
>>             refuse to believe that this idea wasn't thought up years
>>             ago, which leads me to believe there was a reason that it
>>             hasn't been done.
>>
>>             Is there any way we can look this up in the bug database
>>             or something?
>>
>>
>>             On Thu, Jan 16, 2025, 2:28 PM Jens Lideström
>>             <jens at lidestrom.se> wrote:
>>
>>                 Having the result Map.of and Set.of preserve the
>>                 insertion order would
>>                 often be convenient.
>>
>>                 More often than not programs iterate over the
>>                 contents of a maps and
>>                 sets at some point. For example to present the values
>>                 in a GUI, for
>>                 serialisation, or even for error printouts. In all
>>                 those cases having a
>>                 fixed iteration order is much better than having a
>>                 random iteration
>>                 order.
>>
>>                 Often it is even a subtle bug to have a random
>>                 iteration order. For
>>                 example, I ran in to a situation where jdeps printed
>>                 a error message
>>                 containing a list of modules. But the list was in a
>>                 different order on
>>                 each run of the program! It took me a while to figure
>>                 out that it was
>>                 actually the same list. A possible explanation is
>>                 that jdeps is
>>                 implemented using Map.of or Set.of.
>>
>>                 Because of this I think I would be better if the most
>>                 commonly used
>>                 standard collection factories produced collections
>>                 with a fixed
>>                 iteration order.
>>
>>                 Guavas ImmutableMap and ImmutableSet also preserve
>>                 insertion order.
>>
>>                 Regards,
>>                 Jens Lideström
>>
>>
>>                 On 2025-01-16 08:44, Remi Forax wrote:
>>
>>                 > -------------------------
>>                 >
>>                 >> From: "Rafael Winterhalter" <rafael.wth at gmail.com>
>>                 >> To: "core-libs-dev" <core-libs-dev at openjdk.java.net>
>>                 >> Sent: Thursday, January 16, 2025 8:13:17 AM
>>                 >> Subject: Factory methods for SequencedSet and
>>                 SequencedMap
>>                 >
>>                 >> Hello,
>>                 >
>>                 > Hello,
>>                 >
>>                 >> I am happily taking SequencedSet and SequencedMap
>>                 into use, but one
>>                 >> inconvenience I encounter is the lack of factory
>>                 methods for the two.
>>                 >> In code where many (initial) collections have zero
>>                 or one element (for
>>                 >> later aggregation), I now write
>>                 Set.of()/Set.of(one) and
>>                 >> Map.of()/Map.of(key, value), as it makes the code
>>                 shorter and more
>>                 >> readable. Those collections are of course
>>                 implicitly sequenced, but
>>                 >> now I must make the variable type of the
>>                 surrounding monad Set and
>>                 >> Map, and simply assume that a LinkedHashSet or
>>                 LinkedHashMap is used
>>                 >> when a collection of more than one element is set,
>>                 without requiring
>>                 >> the interface type. This does not require any type
>>                 casting, as I rely
>>                 >> on the iteration order only, but the code loses
>>                 some of its
>>                 >> expressiveness.
>>                 >> I did not find any discussion around introducing
>>                 factories for
>>                 >> SequencedSet.of(...) and SequencedMap.of(...),
>>                 similar to those that
>>                 >> exist in the Set and Map interfaces. Was this ever
>>                 considered, and if
>>                 >> not, could it be?
>>                 >
>>                 > Thanks for re-starting that discussion, it was
>>                 talked a bit, but not as
>>                 > it should be.
>>                 >
>>                 > So the issue is that currently we do not have any
>>                 compact, unmodifiable
>>                 > and ordered Set/Map implementation,
>>                 > one use case is when you have data that comes from
>>                 a JSON object as a
>>                 > Map and you want to keep the inserted order, if by
>>                 example the JSON is
>>                 > a config file editable by a human, an other example
>>                 is in unit tests
>>                 > where you want to help the dev to read the output
>>                 of the test so the
>>                 > code that creates a Set/Map and what is outputed by
>>                 the test should be
>>                 > in the same order.
>>                 > Currently there is no good solution for those use
>>                 cases because
>>                 > Set|Map.copyOf() does not keep the ordering.
>>                 >
>>                 > I see two solutions, either we add a new
>>                 > SequenceSet|SequenceMap.of/copyOf, or we change the
>>                 impleemntation of
>>                 > Set|Map.of()/copyOf().
>>                 > Python had gone for the latter solution, which has
>>                 the advantage a
>>                 > being simple from the user POV, but from an
>>                 algorithm expert POV, a Set
>>                 > and a SequencedSet are different concepts we may
>>                 want to emphasis ?
>>                 >
>>                 >> Best regards, Rafael
>>                 >
>>                 > regards,
>>                 > Rémi
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20250117/ccfe8029/attachment-0001.htm>


More information about the core-libs-dev mailing list