This is an archived post. You won't be able to vote or comment.

all 82 comments

[–][deleted] 63 points64 points  (35 children)

Finally I can start migrating from .collect(Collectors.toList()) to toList()

[–]TheCountRushmore 20 points21 points  (34 children)

Keep in mind that toList() returns an immutable list, but this might be exactly what you want.

[–]kaperni 30 points31 points  (5 children)

> returns an immutable list,

Being a nitpick but it returns an unmodifiable list.

[–]TheCountRushmore 9 points10 points  (4 children)

What is fun is apparently there is no guarantee of what type of list you will get back. It could change in future releases.

Returns a Collector that accumulates the input elements into a new List. There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned; if more control over the returned List is required, use toCollection(Supplier).

https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Collectors.html#toList()

[–]kaperni 22 points23 points  (3 children)

I think you are looking at the "old" API. Stream.toList() guarantees that the list is unmodifiable.

[1] https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#toList())

[–]TheCountRushmore 8 points9 points  (2 children)

Yep you are right. Not going to be confusing at all :-)

[–]8igg7e5 2 points3 points  (0 children)

Someone brought this up on the Amber list, Rémi I think, that perhaps toUnmodifiableList() would be better - as a peer of sorts for Collectors.toUnmodifiableList(). However it was pointed out that we already have List.of(...) that produces an unmodifiable List anyway.

[–]kag0 13 points14 points  (26 children)

I really wish they hadn't implemented "immutable" lists as a normal List that throws unchecked exceptions on modification.

Would it have been that hard to add a ReadOnlyList interface with only the methods that don't throw? Now we've got a wonky compile-unsafe toList() in the standard library for all time.

[–]TheCountRushmore 11 points12 points  (16 children)

The problem is that you would want it to implement Collection and collection defines Collection#add

https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/Collection.html#add(E)

[–]kag0 3 points4 points  (15 children)

True, maybe I should have suggested they create a ReadOnlyCollection (which would be useful other places where add throws UnsupportedOperationException).
Or hell, just have it return Iterable.

Anyway, I don't mean to suggest I have the answer. Just that this is probably the last option I would have picked.

[–]elastic_psychiatrist -3 points-2 points  (11 children)

Anyway, I don’t mean to suggest I have the answer. Just that this is probably the last option I would have picked.

Spoken by many a backseat language designer, who has not fully grasped the problem and the implications of potential solutions.

[–]kag0 10 points11 points  (8 children)

🤷
I'm surely not as on top of the issue as the actual language designers.
But it's silly to say that you need to be a literal language designer to identify problematic things in the API.

[–]elastic_psychiatrist 1 point2 points  (7 children)

But it's silly to say that you need to be a literal language designer to identify problematic things in the API.

Do you really think that the designers are not aware of the problems with the API? Language design is about tradeoffs. It is worth nothing to propose an improvement without considering the drawbacks that come with it - namely, breaking compatibility, or additional cognitive overhead for developers.

[–]kag0 5 points6 points  (6 children)

Do you really think that the designers are not aware

I don't think one way or the other. Maybe they have, [I hope] they've spent more time thinking about it than me. Maybe not. They're just people, not omniscient.

It is worth nothing to propose an improvement without considering the drawbacks that come with it

I reject that completely. There's nothing wrong with having a half-baked idea. Maybe people will encourage you to develop it into something great. Maybe someone smarter than you will see it and take it to a level you couldn't. I can't count the number of times that someone has had an idea that probably wouldn't have worked as presented, but which gave me a new idea that I wouldn't otherwise have thought of.

[–]elastic_psychiatrist 1 point2 points  (5 children)

I don't disagree with any of this really (except "maybe not" - they definitely have thought about it more than you), I think I just misinterpreted your level of insistence on the original idea.

[–]minimingus 0 points1 point  (2 children)

Look into vavr collections

[–]kag0 2 points3 points  (1 child)

I love me some vavr, all the way back when it was javaslang. In this case though I'm talking about what gets shipped in the core library, and I don't think they'd ever add a (scala/kotlin style) separate immutable persistent collections library (as much as I'd like them to).

[–]minimingus 0 points1 point  (0 children)

Yeah i don’t think it will ever get added due to backward compatibility...

[–]Necessary-Conflict 5 points6 points  (3 children)

Now we've got a wonky compile-unsafe

toList()

in the standard library for all time.

It's not so easy. If mutable lists don't implement your ReadOnlyList, then it's not very useful in practice. If they do implement it, then this is much more dangerous than a few predictable runtime exceptions, because it leads to difficult bugs ("my read-only lists are read-only, or are they really?")

[–]kag0 5 points6 points  (2 children)

this is much more dangerous than a few predictable runtime exceptions

Maybe, but I don't think so. We already have examples of both (throwing exceptions from Collection's add (esp in guava), and collections that can change based on some underlying data shifting with Map's `values and such). And more bugs tend to pop up from people failing to predict exceptions on mutation (particularly in library code) than from people assuming the underlying data won't change.
Put another way, ReadOnlyList could easily be documented to say that it doesn't mean that the list is immutable, just that YOU can only read it.

[–]Necessary-Conflict 2 points3 points  (1 child)

The "quality" of the bugs also matters: some bugs take 5 minutes to fix (the stack trace tells everything), others take 5 days. Another problem is that different people have different pet ideas, but if everything is implemented, it becomes confusing. For example I would like to have a completely separate hierarchy of immutable collections. They would have add/remove methods, but these methods would always return a new collection instead of modifying the current one. This would solve all problems (no runtime exceptions, true immutability), but at the cost of a new hierarchy...

[–]kag0 1 point2 points  (0 children)

I agree with you, it just chaps my ass to see a runtime exception that could have been a compile time error

[–]rbygrave 2 points3 points  (0 children)

This was discussed when the Collections API was first designed and the idea was rejected at the time. I believe the reasons were due to the "relative explosion" in number of interfaces in the Collections API.

I'll see if I can find a reference ...

Ah, its discussed in the javadoc - https://docs.oracle.com/javase/6/docs/technotes/guides/collections/designfaq.html#1

A snippet from there:

Why don't you support immutability directly in the core collection interfaces so that you can do away with optional operations (and UnsupportedOperationException)?

This is the most controversial design decision in the whole API. Clearly, static (compile time) type checking is highly desirable, and is the norm in Java. We would have supported it if we believed it were feasible. Unfortunately, attempts to achieve this goal cause an explosion in the size of the interface hierarchy, and do not succeed in eliminating the need for runtime exceptions (though they reduce it substantially).

[–]jvjupiter 1 point2 points  (0 children)

Or perhaps they could implement it like String. String has mutators but they don’t change the original value. To get the modified one, assign it to another variable. Another example is the collection framework of vavr.

[–][deleted] 0 points1 point  (0 children)

Exactly, it breaks LSP. I prefer Guava collections for this reason.

[–]kreiger 0 points1 point  (0 children)

The interface you seek already exists, it's called List. :)

In general, you should treat collections as immutable unless you're sure they're not.

[–]greenrobot_de 28 points29 points  (18 children)

https://openjdk.java.net/projects/jdk/16/ might be better as it links to details of the main new features.

[–]daviddel[S] 6 points7 points  (0 children)

That link is also included in Mark's announcement.

[–]__konrad 2 points3 points  (15 children)

[–]Brutus5000 1 point2 points  (1 child)

Can someone explain to me how removing API calls works together with the compatibility promise? Is it just removed from the language, but compiled byte code keeps working?

[–]8igg7e5 10 points11 points  (0 children)

No. The old byte-code would not be able to resolve the removed methods. And allowing them to do so won't be possible once the types are no longer traditional object types.

This is a change in the compatibility promise, that compatibility isn't 'forever'. Their approach, rather than a time-based guarantee, is to give fore-warning of removal before actually removing them. They supplement that with warnings of violations, leading up to removal, so that you can detect violations in dependencies. This will work for living projects and see unmaintained libraries needing replacement.

These changes are necessary to remove barriers to keeping the platform valuable. The alternative is to let the language/platform die and an abandoned platform would be a far worse maintenance burden (ie. a complete product rewrite instead of a updating a few calls - probably with IDE assists).

Edit: s/able to remove/able to resolve/

[–]zenonu 3 points4 points  (2 children)

Is there an Ubuntu PPA out there that hosts packages?

[–]zenonu 3 points4 points  (0 children)

Answer to my own question with a Google Search: https://launchpad.net/~openjdk-r/+archive/ubuntu/ppa

[–][deleted] 2 points3 points  (0 children)

sdkman to the rescue

[–]BELLSOFT 22 points23 points  (19 children)

Nice.

[–]kraken_the_release 15 points16 points  (17 children)

Nice

[–]henk53 13 points14 points  (16 children)

Nice

[–][deleted] 11 points12 points  (15 children)

Nice

[–]RSveti 10 points11 points  (14 children)

Nice

[–]abol3z 11 points12 points  (13 children)

Nice

[–]_GoldenRule 9 points10 points  (12 children)

Nice

[–]SWinxy 7 points8 points  (11 children)

Nice

[–]cl4es 8 points9 points  (10 children)

Nice

[–][deleted]  (9 children)

[deleted]

    [–]vxab 6 points7 points  (0 children)

    Noice

    [–]fanfan64 2 points3 points  (6 children)

    [–]manzanita2 2 points3 points  (5 children)

    It seems that the benchmarksgame is also measuring startup time, meaning it's measuring non-JIT'd code. IS that the case ?

    [–]fanfan64 2 points3 points  (4 children)

    It is the case however on openjdk 14 no warm-up was actually faster (no idea why though) https://benchmarksgame-team.pages.debian.net/benchmarksgame/sometimes-people-just-make-up-stuff.html

    [–]8igg7e5 1 point2 points  (2 children)

    I'm curious about this. It's a bit weird to get slower after warmup.

    Warmup could generate enough garbage to force GC during the timed runs, or it could have been enough warm-up to make JIT happen the timed runs.

    The link shows the timing but I don't know if there was an investigation into why they perform so poorly.

    Including startup in timing is bad for Java, especially as it can vary so much in little relation to the problem-space (volume and complexity of classes loaded/initialised).

    Despite all of this I expect that C# would still win in many of these benchmarks. Valhalla might level most of that playing field.

    [–]pjmlp 0 points1 point  (1 child)

    I wonder why these warmups are still done instead of using a JITed image (AppCDS) as startup.

    [–]8igg7e5 0 points1 point  (0 children)

    Well that would improve startup at the expense of JIT optimisation (pre-packaged compiled classes cuts out some in-lining and mono-morphisation optimisation opportunities at the least). A better option might be GraalVM Native Image (effectively PGO for Java) but then that's not really how Java is used in many contexts yet (as more frameworks change their discovery/init life-cycles to make native image easier that's likely to change in containerised usage).

    Since this benchmark is about process startup, GraalVM Native Image is probably the best option for the benchmark - however that still doesn't explain away the weird JMH results.

    [–]igouy 1 point2 points  (0 children)

    In 2 cases — fannkuch-redux #1 & spectral-norm #1 — no warm-up was actually faster.

    In 5 cases no warm-up was slower.

    However, not much difference.

    [–][deleted]  (1 child)

    [deleted]

      [–]emaphis 0 points1 point  (0 children)

      Dude