<div dir="ltr">All trivial collection operations scream stream api and all stream api operations imply a full copy or at least a full scan.<br><br>> <span style="color:rgb(0,0,0);font-family:arial,helvetica,sans-serif;font-size:16px">so having trouble to write this kind of code is more a feature than an issue :)<br><br>I love all Java code equally</span></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Aug 24, 2022 at 11:21 AM <<a href="mailto:forax@univ-mlv.fr">forax@univ-mlv.fr</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div style="font-family:arial,helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><div><br></div><div><br></div><hr id="gmail-m_-3449610018727599261zwchr"><div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><b>From: </b>"Ethan McCue" <<a href="mailto:ethan@mccue.dev" target="_blank">ethan@mccue.dev</a>><br><b>To: </b>"Remi Forax" <<a href="mailto:forax@univ-mlv.fr" target="_blank">forax@univ-mlv.fr</a>><br><b>Cc: </b>"John Hendrikx" <<a href="mailto:john.hendrikx@gmail.com" target="_blank">john.hendrikx@gmail.com</a>>, "core-libs-dev" <<a href="mailto:core-libs-dev@openjdk.org" target="_blank">core-libs-dev@openjdk.org</a>><br><b>Sent: </b>Wednesday, August 24, 2022 4:27:01 PM<br><b>Subject: </b>Re: Proposal: Collection mutability marker interfaces<br></blockquote></div><div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><div dir="ltr">> so it's perhaps better to call add() inside a try/catch on UnsupportedOperationException.<div><br>As much as sin is wrong, sketching out what that might look like... forgive the toyness of the example</div></div></blockquote><div><br></div><div><br></div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><div dir="ltr"><div><br><br><br>VS<br><br><br><br>final class Ex {<br> private Ex() {}<br><br> /*<br> * Adds the odd numbers from 1 to 10 to the List then makes all the odds even.<br> *<br> * Takes ownership of the list, avoids making copies if it doesn't need to<br> */<br> static List<Integer> addOdds(List<Integer> l) {<br> for (int i = 1; i <= 10; i++) {<br> try {<br> l.add(i);<br> } catch (UnsupportedOperationException e) {<br> l = new ArrayList<>(l);</div></div></blockquote><div><br></div><div>i -= 1; // restart with an ArrayList<br></div><div><br></div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><div dir="ltr"><div><br> }<br> }<br><br> for (int i = 0; i < l.size(); i++) {<br> if (l.get(i) % 2 == 1) {<br> try {<br> l.set(i, l.get(i) + 1);<br> } catch (UnsupportedOperationException e) {<br> l = new ArrayList<>(l);<br> }<br> }<br> }<br> }<br>}</div></div></blockquote><div><br></div><div>as Roger said, there is no way in Java to know if the caller has not kept a reference (unlike Rust),</div><div>so having trouble to write this kind of code is more a feature than an issue :)<br></div><div><br></div><div>This kind of examples scream the Stream API, which has the correct semantics<br></div><div> IntStream.rangeClosed(1, 10).map(i -> i % 2 == 0? i + 1: i).boxed().toList()<br></div><div><br></div><div>Rémi<br></div><div><br></div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><div dir="ltr"><div><br><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Aug 24, 2022 at 10:03 AM Remi Forax <<a href="mailto:forax@univ-mlv.fr" target="_blank">forax@univ-mlv.fr</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div style="font-family:arial,helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br><br><hr id="gmail-m_-3449610018727599261gmail-m_-4085950217033255430zwchr"><div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><b>From: </b>"Ethan McCue" <<a href="mailto:ethan@mccue.dev" target="_blank">ethan@mccue.dev</a>><br><b>To: </b>"John Hendrikx" <<a href="mailto:john.hendrikx@gmail.com" target="_blank">john.hendrikx@gmail.com</a>><br><b>Cc: </b>"core-libs-dev" <<a href="mailto:core-libs-dev@openjdk.org" target="_blank">core-libs-dev@openjdk.org</a>><br><b>Sent: </b>Wednesday, August 24, 2022 3:38:26 PM<br><b>Subject: </b>Re: Proposal: Collection mutability marker interfaces<br></blockquote></div><div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><div dir="auto">A use case that doesn't cover is adding to a collection.<div dir="auto"><br></div><div dir="auto">Say as part of a method's contract you state that you take ownership of a List. You aren't going to copy even if the list is mutable.</div><div dir="auto"><br></div><div dir="auto">Later on, you may want to add to the list. Add is supported on ArrayList so you don't need to copy and replace your reference, but you would if the list you were given was made with List.of or Arrays.asList</div></div></blockquote><br><div>You can ask if the spliterator considers the collection as immutable or not,<br></div><div> list.spliterator().hasCharacteristics(Spliterator.IMMUTABLE)<br></div><br><div>sadly, List.of()/Map.of() does not report the spliterator characteristics correctly (the spliterator implementations are inherited from AbstracList/AbstractMap).<br></div><br><div>so it's perhaps better to call add() inside a try/catch on UnsupportedOperationException.<br></div><br><div>Rémi<br></div><br><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Aug 24, 2022, 8:13 AM John Hendrikx <<a href="mailto:john.hendrikx@gmail.com" target="_blank">john.hendrikx@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<p>Would it be an option to not make the receiver responsible for
the decision whether to make a copy or not? Instead put this
burden (using default methods) on the various collections?</p>
<p>If List/Set/Map had a method like this:</p>
<p> List<T> immutableCopy(); // returns a (shallow)
immutable copy if list is mutable (basically always copies, unless
proven otherwise)</p>
<p>Paired with methods on Collections to prevent collections from
being modified:</p>
<p> Collections.immutableList(List<T>)</p>
<p>This wrapper is similar to `unmodifiableList` except it
implements `immutableCopy` as `return this`.<br>
</p>
<p>Then for the various scenario's, where `x` is an untrusted source
of List with unknown status:<br>
</p>
<p> // Create a defensive copy; result is a private list that
cannot be modified:<br>
</p>
<p> List<T> y = x.immutableCopy(); <br>
</p>
<p> // Create a defensive copy for sharing, promising it won't
ever change:<br>
</p>
<p>
List<T> y = Collections.immutableList(x.immutableCopy());
<br>
</p>
<p> // Create a defensive copy for mutating:</p>
<p> List<T> y = new ArrayList<>(x); // same as
always</p>
<p> // Create a mutable copy, modify it, then expose as
immutable:</p>
<p> List<T> y = new ArrayList<>(x); // same as
always</p>
<p> y.add( <some element> ); <br>
</p>
<p> List<T> z = Collections.immutableList(y);</p>
<p> y = null; // we promise `z` won't change again by clearing
the only path to mutating it!<br>
</p>
<p>The advantage would be that this information isn't part of the
type system where it can easily get lost. The actual
implementation knows best whether a copy must be made or not.</p>
<p>Of course, the immutableList wrapper can be used incorrectly and
the promise here can be broken by keeping a reference to the
original (mutable) list, but I think that's an acceptable
trade-off.</p>
<p>--John</p>
<p>PS. Chosen names are just for illustration; there is some
discussion as what "unmodifiable" vs "immutable" means in the
context of collections that may contain elements that are mutable.
In this post, immutable refers to shallow immutability .</p>
<div>On 24/08/2022 03:24, Ethan McCue wrote:<br>
</div>
<blockquote>
<div dir="auto">Ah, I'm an idiot.
<div dir="auto"><br>
</div>
<div dir="auto">There is still a proposal here
somewhere...maybe. right now non jdk lists can't participate
in the special casing?</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Tue, Aug 23, 2022, 9:00 PM
Paul Sandoz <<a href="mailto:paul.sandoz@oracle.com" rel="noreferrer" target="_blank">paul.sandoz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">List.copyOf
already does what you want.<br>
<br>
<a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068" rel="noreferrer noreferrer noreferrer" target="_blank">https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068</a><br>
<a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168" rel="noreferrer noreferrer noreferrer" target="_blank">https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168</a><br>
<br>
Paul.<br>
<br>
> On Aug 23, 2022, at 4:49 PM, Ethan McCue <<a href="mailto:ethan@mccue.dev" rel="noreferrer noreferrer" target="_blank">ethan@mccue.dev</a>> wrote:<br>
> <br>
> Hi all,<br>
> <br>
> I am running into an issue with the collections framework
where I have to choose between good semantics for users and
performance.<br>
> <br>
> Specifically I am taking a java.util.List from my users
and I need to choose to either<br>
> * Not defensively copy and expose a potential footgun
when I pass that List to another thread<br>
> * Defensively copy and make my users pay an unnecessary
runtime cost.<br>
> <br>
> What I would really want, in a nutshell, is for
List.copyOf to be a no-op when used on lists made with
List.of().<br>
> <br>
> Below the line is a pitch I wrote up on reddit 7 months
ago for a mechanism I think could accomplish that. My goal is
to share the idea a bit more widely and to this specific
audience to get feedback.<br>
> <br>
> <a href="https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3" rel="noreferrer noreferrer noreferrer" target="_blank">https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3</a>
<br>
> <br>
> Important also for context is Ron Pressler's comment
above.<br>
> --------------<br>
> <br>
> What if the collections api added more marker interfaces
like RandomAccess?<br>
> <br>
> It's already a common thing for codebases to make
explicit null checks at error boundaries because the type
system can't encode null | List<String>. <br>
> <br>
> This feels like a similar problem.<br>
> If you have a List<T> in the type system then you
don't know for sure you can call any methods on it until you
check that its not null. In the same way, there is a set of
methods that you don't know at the type/interface level if you
are allowed to call.<br>
> <br>
> If the List is actually a __<br>
> Then you can definitely call<br>
> And you know other reference holders might call<br>
> And you can confirm its this case by<br>
> null<br>
> no methods<br>
> no methods<br>
> list == null<br>
> List.of(...)<br>
> get, size<br>
> get, size<br>
> ???<br>
> Collections.unmodifiableList(...)<br>
> get, size<br>
> get, size, add, set<br>
> ???<br>
> Arrays.asList(...)<br>
> get, size, set<br>
> get, size, set<br>
> ???<br>
> new ArrayList<>()<br>
> get, size, add, set<br>
> get, size, add, set<br>
> ???<br>
> While yes, there is no feasible way to encode these
things in the type system. Its not impossible to encode it at
runtime though.<br>
> interface FullyImmutable {<br>
> // So you know the existence of this implies the absence<br>
> // of the others<br>
> default Void cantIntersect() { return null; }<br>
> }<br>
> <br>
> interace MutationCapability {<br>
> default String cantIntersect() { return ""; }<br>
> }<br>
> <br>
> interface Addable extends MutationCapability {}<br>
> interface Settable extends MutationCapability {}<br>
> <br>
> If the List is actually a __<br>
> Then you can definitely call<br>
> And you know other reference holders might call<br>
> And you can confirm its this case by<br>
> null<br>
> no methods<br>
> no methods<br>
> list == null<br>
> List.of(...)<br>
> get, size<br>
> get, size<br>
> instanceof FullyImmutable<br>
> Collections.unmodifiableList(...)<br>
> get, size<br>
> get, size, add, set<br>
> !(instanceof Addable) && !(instanceof Settable)<br>
> Arrays.asList(...)<br>
> get, size, set<br>
> get, size, set<br>
> instanceof Settable<br>
> new ArrayList<>()<br>
> get, size, add, set<br>
> get, size, add, set<br>
> instanceof Settable && instanceof Addable<br>
> In the same way a RandomAccess check let's
implementations decide whether they want to try an alternative
algorithm or crash, some marker "capability" interfaces would
let users of a collection decide if they want to clone what
they are given before working on it.<br>
> <br>
> <br>
> --------------<br>
> <br>
> So the applicability of this would be that the list
returned by List.of could implement FullyImmutable, signifying
that there is no caller which might have a mutable handle on
the collection. Then List.of could check for this interface
and skip a copy.<br>
> <br>
> <br>
<br>
</blockquote>
</div>
</blockquote>
</div>
</blockquote></div></blockquote></div></div></div></blockquote></div><br></blockquote></div></div></div></blockquote></div>