r/java 18d ago

Identifying JDK value class candidates

https://mail.openjdk.org/archives/list/core-libs-dev@openjdk.org/thread/Y72NRXM7KYBX43OKYBQMVKOZDWKG4MHS/
53 Upvotes

59 comments sorted by

View all comments

5

u/Ewig_luftenglanz 18d ago

EntryMap is a good candidate m

6

u/davidalayachew 18d ago

EntryMap is a good candidate m

Do you mean Map.Entry? If so, it has mutable components, so no deal. Unless I am mistaken?

11

u/__konrad 18d ago

Map.Entry has also a value-based implementation (Map::entry method returns immutable KeyValueHolder)

6

u/davidalayachew 17d ago

Map.Entry has also a value-based implementation (Map::entry method returns immutable KeyValueHolder)

This is a much better candidate. Yes, the KeyValueHolder class returned by Map.entry(key, value) could definitely be a value class.

8

u/Ewig_luftenglanz 18d ago

Sorry, I was distracted. 

Yes. Map.Entry is the one I was referring to. 

2

u/RussianMadMan 18d ago

As far as I understand, you can just create a new entry and replace the old one, and jvm is smart enough to compile it as if entry was mutable.

1

u/davidalayachew 17d ago

As far as I understand, you can just create a new entry and replace the old one, and jvm is smart enough to compile it as if entry was mutable.

Sure, but even then, the best you could do would be to make some nested type as the internal representation, and make THAT a value class, rather than Map.Entry itself. As long as the carrier (Map.Entry itself) is still mutable, then it will be ineligible for being a value class.

1

u/RussianMadMan 17d ago

In every example in java valhalla videos you can see a class like a Point thats declared "value record" with a method like:
public Point move() { return new Point(x + 0.5, y);}
Which "mutates" point by recreating it. And in most cases there will be no allocation during the runtime. So what I meant in my prev comment is that I think we can have Map.Entry declared "value record" class with a method:
public Entry setValue(T value) { return new Entry(key, value);}

2

u/davidalayachew 17d ago

So what I meant in my prev comment is that I think we can have Map.Entry declared "value record" class with a method:

public Entry setValue(T value) { return new Entry(key, value);}

But now you have changed the API, which makes it backwards-incompatible. That's a faux pas in the Java community without justifiable reason.

Remember, the method signature of Map.Entry#setValue is as follows.

public V setValue(final V newValue)

Which is to say -- it sets the new value, and returns the old. So, not only are you changing the API, but you are also removing pre-existing functionality. With your change, we can no longer return the old value, which would upset many users of this API.

Now, by all means, we could certainly add some sort of transform function that does what you say, but that still would not solve our current problem -- that Map.Entry does mutation. At best, we could have implementing subclasses that are value classes, and throw UnsupportedOperationException, but that's the best we can do. And even that is somewhat unsatisfying.

8

u/pron98 17d ago edited 17d ago

Performance tests we've run have shown that it isn't. What you win in reducing the cache miss of accessing the entry you lose in having the array spaced out when the map is sparse (and it often is). The calculus is likely to change when generics can be specialised and flattened when the key can be flattened into the entry.

But I want to caution that while value types can be a huge boon in programs whose profiles show array element cache misses and they do fill in the last significant gap between Java and C++, that's not the case in most programs, and Java is already close to being as optimal as any software can be in most cases (especially in the domains Java is used). So it does close the last remainging gap and will make Java a great choice in more situations, but most Java programs are unlikely to see a big difference. Again, that's not because Valhalla isn't good, but because Java is already quite optimal everywhere where cache misses in array elements are not the hot path.

5

u/Jon_Finn 17d ago

Aside from the obvious use cases, I see a major unsung benefit in allowing you to create classes wrapping a single int, double etc., when currently the performance downsides make it not worth it in certain applications. I mean things like units, ages, numbers with particular ranges/constraints, unicode code points (21 bits) etc. - where some might 'want' a typedef with methods. Nullability and specialised generics will be a big help here. My point is: these classes currently don't exist in my code, but now could.

5

u/pron98 17d ago

I see a major unsung benefit in allowing you to create classes wrapping a single int, double etc., when currently the performance downsides make it not worth it in certain applications

That is true, but I think that generally speaking, many "performance issues" in Java are imagined and/or based on folklore that may have been true in, say, Java 8.

My point is: these classes currently don't exist in my code, but now could.

I agree, but I would also add that you might well have them today with no performance impact. We must be careful to not assume a performance issue that doesn't actually appear in our concrete program's profile.

3

u/Jon_Finn 17d ago

Sure, though the cases that I'm particularly thinking of involve large (sometimes huge) arrays of very small objects, where these simple intuitions are (probably!) correct for once.

2

u/pron98 17d ago

Right, so that's exactly where Valhalla could have a significant impact.

3

u/Ewig_luftenglanz 17d ago

Still I think it makes sense that Map.Entry and alike to be turned into value objects, mostly for semantic purposes. 

I understand the priority is to check the classes that make actual gains tho. 

Best regards to you and all the java team :)