<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<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 class="moz-cite-prefix">On 24/08/2022 03:24, Ethan McCue wrote:<br>
</div>
<blockquote type="cite"
cite="mid:CA+NR86jY8irHa+CxUgLAGH12=vmL3kBY4xzcNkqFjN+n5gbVTw@mail.gmail.com">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<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"
moz-do-not-send="true" class="moz-txt-link-freetext">paul.sandoz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;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" target="_blank"
moz-do-not-send="true" class="moz-txt-link-freetext">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" target="_blank"
moz-do-not-send="true" class="moz-txt-link-freetext">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" target="_blank"
rel="noreferrer" moz-do-not-send="true"
class="moz-txt-link-freetext">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" target="_blank"
moz-do-not-send="true">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>
</body>
</html>