Fwd: Mechanism to maintain backwards compatibility when moving classes to different packages
Friendly reminder :) -- Forwarded message - From: Jacob Glickman Date: Sat, Jun 22, 2019 at 4:42 PM Subject: Mechanism to maintain backwards compatibility when moving classes to different packages To: Yesterday, Mark Reinhold introduced the idea of a java.util.random subpackage[1]. Obviously, moving java.util.Random into that subpackage is not currently an option, as that would break backwards compatibility. Assuming the subpackage is created, it would make sense to ultimately move java.util.Random into it. For that reason, I propose a new language feature that could make this possible: package java.util.random previously java.util; The syntax isn't important at the moment, but this mechanism should allow for users to run previously-compiled code with newer versions of Java, even if their code points to a class that has since been moved to a different package. To eliminate potential bugs, users compiling new code shouldn't be allowed to reference java.util.Random, even if java.util.random.Random states that it previously resided in java.util. I'm curious what you all would think of this, as it is not just applicable to this single example. There have been plenty of times that I've realized that my packages were named badly, but I'm forced to stick with that naming (unless I want my users to have to modify their code). Thanks, Jacob Glickman [1]: http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-June/060995.html
Mechanism to maintain backwards compatibility when moving classes to different packages
Yesterday, Mark Reinhold introduced the idea of a java.util.random subpackage[1]. Obviously, moving java.util.Random into that subpackage is not currently an option, as that would break backwards compatibility. Assuming the subpackage is created, it would make sense to ultimately move java.util.Random into it. For that reason, I propose a new language feature that could make this possible: package java.util.random previously java.util; The syntax isn't important at the moment, but this mechanism should allow for users to run previously-compiled code with newer versions of Java, even if their code points to a class that has since been moved to a different package. To eliminate potential bugs, users compiling new code shouldn't be allowed to reference java.util.Random, even if java.util.random.Random states that it previously resided in java.util. I'm curious what you all would think of this, as it is not just applicable to this single example. There have been plenty of times that I've realized that my packages were named badly, but I'm forced to stick with that naming (unless I want my users to have to modify their code). Thanks, Jacob Glickman [1]: http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-June/060995.html
Stream Method Proposal: long count(Predicate predicate)
Hello! I see myself having to often call count() as a terminal operation on a Stream immediately after performing a filter operation. How feasible would it be to add an overloaded count() method that accepts a Predicate, which it uses as a filter before returning the count of elements in the Stream? If this is supported, I'd gladly create the webrev & tests for it! I suppose the method signature can be something along the lines of: long count(Predicate predicate) It would also seem reasonable to give this method to IntStream, DoubleStream, and LongStream, but allowing them to use IntPredicate, DoublePredicate, and LongPredicate respectively. Thanks, Jacob Glickman
Re: Stream Method Proposal: long count(Predicate predicate)
There was a typo in my last e-mail: `.count("Java::equals")` should be `.count("Java"::equals)` Thanks, Jacob Glickman On Thu, Nov 8, 2018 at 9:02 AM Jacob Glickman wrote: > Tagir, > > Nothing is wrong with it, but I think the addition of the convenience > method(s) would help to improve readability in some cases. Personally, I'd > much rather have the option of writing `.count("Java::equals")` than > `.filter("Java"::equals).count()`. As Zheka stated, this type of > convenience method could also be useful for findFirst, in addition to > findAny, distinct, etc. > > Thanks, > > Jacob Glickman > > On Thu, Nov 8, 2018 at 4:46 AM Tagir Valeev wrote: > >> What's wrong with `filter(predicate).count()`? Saving nine characters? >> >> With best regards, >> Tagir Valeev. >> On Thu, Nov 8, 2018 at 8:02 AM Jacob Glickman >> wrote: >> > >> > Hello! >> > >> > I see myself having to often call count() as a terminal operation on a >> > Stream immediately after performing a filter operation. How feasible >> would >> > it be to add an overloaded count() method that accepts a Predicate, >> which >> > it uses as a filter before returning the count of elements in the >> Stream? >> > If this is supported, I'd gladly create the webrev & tests for it! >> > >> > I suppose the method signature can be something along the lines of: >> > >> > long count(Predicate predicate) >> > >> > It would also seem reasonable to give this method to IntStream, >> > DoubleStream, and LongStream, but allowing them to use IntPredicate, >> > DoublePredicate, and LongPredicate respectively. >> > >> > Thanks, >> > >> > Jacob Glickman >> >
Re: Stream Method Proposal: long count(Predicate predicate)
Tagir, Nothing is wrong with it, but I think the addition of the convenience method(s) would help to improve readability in some cases. Personally, I'd much rather have the option of writing `.count("Java::equals")` than `.filter("Java"::equals).count()`. As Zheka stated, this type of convenience method could also be useful for findFirst, in addition to findAny, distinct, etc. Thanks, Jacob Glickman On Thu, Nov 8, 2018 at 4:46 AM Tagir Valeev wrote: > What's wrong with `filter(predicate).count()`? Saving nine characters? > > With best regards, > Tagir Valeev. > On Thu, Nov 8, 2018 at 8:02 AM Jacob Glickman wrote: > > > > Hello! > > > > I see myself having to often call count() as a terminal operation on a > > Stream immediately after performing a filter operation. How feasible > would > > it be to add an overloaded count() method that accepts a Predicate, which > > it uses as a filter before returning the count of elements in the Stream? > > If this is supported, I'd gladly create the webrev & tests for it! > > > > I suppose the method signature can be something along the lines of: > > > > long count(Predicate predicate) > > > > It would also seem reasonable to give this method to IntStream, > > DoubleStream, and LongStream, but allowing them to use IntPredicate, > > DoublePredicate, and LongPredicate respectively. > > > > Thanks, > > > > Jacob Glickman >
Stream Method Proposal: long count(Predicate predicate)
Hello! I see myself having to often call count() as a terminal operation on a Stream immediately after performing a filter operation. How feasible would it be to add an overloaded count() method that accepts a Predicate, which it uses as a filter before returning the count of elements in the Stream? If this is supported, I'd gladly create the webrev & tests for it! I suppose the method signature can be something along the lines of: long count(Predicate predicate) It would also seem reasonable to give this method to IntStream, DoubleStream, and LongStream, but allowing them to use IntPredicate, DoublePredicate, and LongPredicate respectively. Thanks, Jacob Glickman
Re: BiCollector
Agreed. Not sure if this has been suggested, but what about duplex(ing)? On Mon, Jun 18, 2018 at 5:29 PM Brian Goetz wrote: > "bisecting" sounds like it sends half the elements to one collector and > half to the other ... > > "tee" might be a candidate, though it doesn't follow the `ing > convention. "teeing" sounds dumb. > > > > On 6/15/2018 7:36 PM, Paul Sandoz wrote: > > Hi Tagir, > > > > This is looking good. > > > > My current favorite name for the factory method is “bisecting” since we > are dividing the collector into two collectors, the results of which are > then merged. > > > > Suggested first paragraph of the factory method: > > > >"Returns a collector that passes the input elements to two specified > collectors and merges their results with the specified merge function.” > > > > Paul. > > > > > >> On Jun 15, 2018, at 4:26 AM, Tagir Valeev wrote: > >> > >> Hello! > >> > >> I created a preliminary webrev of my own implementation (no testcases > yet): > >> http://cr.openjdk.java.net/~tvaleev/patches/pairing/webrev/ > >> If anybody wants to sponsor my implementation, I will happily log an > issue and write tests. > >> > >> The name "pairing" was invented by me, but as I'm not a native English > speaker I cannot judge whether it's optimal, so better ideas are welcome. > >> Also I decided to remove accumulator types from public type variables. > They do not add anything to type signature, only clutter it > >> increasing the number of type parameters from 4 to 6. I think it was a > mistake to expose the accumulator type parameter in other cascading > collectors > >> like filtering(), collectingAndThen(), groupingBy(), etc. I'm not > insisting though, if you feel that conformance to existing collectors is > >> more important than simplicity. > >> > >> With best regards, > >> Tagir Valeev. > >> > >> On Fri, Jun 15, 2018 at 5:05 AM Brian Goetz > wrote: > >> > >>> Well, I don't see the need to pack the two results into a Map.Entry > >>> (or any similar) container as a drawback. > >> From an "integrity of the JDK APIs" perspective, it is unquestionably > a > >> drawback. These items are not a Key and an associated Value in a Map; > >> it's merely pretending that Map.Entry really means "Pair". There's a > >> reason we don't have a Pair class in the JDK (and no, let's not reopen > >> that now); using something else as a Pair proxy that is supposed to have > >> specific semantics is worse. (It's fine to do this in your own code, but > >> not in the JDK. Different standards for code that has different > audiences.) > >> > >> Tagir's proposed sidestepping is nice, and it will also play nicely with > >> records, because then you can say: > >> > >>record NameAndCount(String name, int count); > >> > >>stream.collect(pairing(collectName, collectCount, > NameAndCount::new)); > >> > >> and get a more properly abstract result out. And its more in the spirit > >> of existing Collectors. If you want to use Map.Entry as an > >> _implementation detail_, that's fine. > >> > >> I can support this form. > >> > >>> I also don't see a larger abstraction like BiStream as a natural fit > >>> for a similar thing. > >> I think the BiStream connection is mostly tangential. We tried hard to > >> support streams of (K,V) pairs when we did streams, as Paul can attest, > >> but it was a huge complexity-inflater and dropping this out paid an > >> enormous simplification payoff. > >> > >> With records, having streams of tuples will be simpler to represent, but > >> no more performant; it will take until we get to value types and > >> specialized generics to get the performance we want out of this. > >> > >> > >
Discussion: Introduce a `Stream.reject` method, the opposite of `Stream.filter`
Seeing as `Predicate.not` was just proposed, I think it would be a good idea to also introduce `Stream.reject`, which would be logically equivalent to calling `Stream.filter` with a negated `Predicate`, but with fewer method calls. Instead of: Stream.of("", "A", "B", "C") .filter(s -> !s.isEmpty()) .count(); You could call: Stream.of("", "A", "B", "C") .reject(String::isEmpty) .count(); I welcome any suggestions for better names other than `reject`. If this suggestion is supported, then I'll happily submit an RFE and the code, tests, etc. - Jacob
Discussion: Efficient ByteBuffer -> String that avoids additional copying
Paul, Would you mind explaining more about what you're looking for regarding this? I'm curious if `ByteBuffer#getString` is what you're after, or rather something similar to `String.from(ByteBuffer)`. I'll definitely have a look at jdk.internal.misc.SharedSecrets in the meantime. Thanks! - Jacob >Hi Jacob, >I do have one idea (that i don’t think is currently represented as a bug, though i have not searched JBS), if you are willing to take it on. It will require some investigation, and careful testing, it’s not necessarily a starter bug :-), but i can help guide. >Investigate new methods to more efficiently support ByteBuffer -> String method on ByteBuffer that can avoid additional copying and makeappropriate use of charsets given String’s compact string support. You need to look at jdk.internal.misc.SharedSecrets for clues on how to trampoline between the nio and lang packages. >Paul.
[PATCH] 8202521: Add overloaded methods of Map#compute, Map#computeIfAbsent, Map#computeIfPresent
Hi Paul, Thank you for the response! I understand I should have discussed this before submitting a patch, and will do so from now on. I would be happy to contribute to fixing some starter bugs, and I'd be very grateful if you are able point me to some! - Jacob
[PATCH] 8202521: Add overloaded methods of Map#compute, Map#computeIfAbsent, Map#computeIfPresent
Hello! This is my first patch, so I apologize in advance if I've done anything incorrectly (including botching the formatting of the mercurial diff below). I think it would be beneficial to have an overloaded `Map.computeIfAbsent` method where the computed value does not depend on the key; for that reason, I submitted [1]. By taking a `Supplier`, this will allow users to utilize method references where applicable. For example, if they create a `Map>`, then the following can be used when computing values: map.computeIfAbsent("key", ArrayList::new).add(1); I originally planned to add overloaded methods for `Map.computeIfPresent` and `Map.compute` as well, but it would make more sense for them to take a `Function` (instead of a `Supplier`) if the computed value does not depend on the key. If this patch is successful, then I'll go forward with that planned change. To clarify, an overloaded `Map.computeIfAbsent` method was the only change, along with the unit test. I used JMH to benchmark my implementation versus an inlined implementation (not calling the existing `Map.computeIfAbsent` with the result of the supplier), and the difference was negligible (maybe the compiler inlined it?). Please let me know if I should inline it so I can change my implementation. [1]: https://bugs.openjdk.java.net/browse/JDK-8202521 -- # HG changeset patch # User jhg023 # Date 1525366367 25200 # Thu May 03 09:52:47 2018 -0700 # Node ID 395def2871e8d077b382d722efc59b38373327d1 # Parent ea246151be08995543d0c9281099608bc9207a19 8202521: Add overloaded methods of Map#compute, Map#computeIfAbsent, Map#computeIfPresent Summary: Added "Map.computeIfAbsent(K key, Supplier supplier)" Reviewed-by: N/A Contributed-by: Jacob Glickman diff -r ea246151be08 -r 395def2871e8 src/java.base/share/classes/java/util/Hashtable.java --- a/src/java.base/share/classes/java/util/Hashtable.java Wed May 02 11:21:27 2018 -0700 +++ b/src/java.base/share/classes/java/util/Hashtable.java Thu May 03 09:52:47 2018 -0700 @@ -29,6 +29,7 @@ import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.BiFunction; +import java.util.function.Supplier; import jdk.internal.misc.SharedSecrets; /** @@ -1054,6 +1055,21 @@ * {@inheritDoc} * * This method will, on a best-effort basis, throw a + * {@link java.util.ConcurrentModificationException} if the + * supplier modified this map during computation. + * + * @throws ConcurrentModificationException if it is detected that the + * supplier modified this map + */ +@Override +public synchronized V computeIfAbsent(K key, Supplier supplier) { +return computeIfAbsent(key, k -> supplier.get()); +} + +/** + * {@inheritDoc} + * + * This method will, on a best-effort basis, throw a * {@link java.util.ConcurrentModificationException} if the remapping * function modified this map during computation. * diff -r ea246151be08 -r 395def2871e8 src/java.base/share/classes/java/util/Map.java --- a/src/java.base/share/classes/java/util/Map.javaWed May 02 11:21:27 2018 -0700 +++ b/src/java.base/share/classes/java/util/Map.javaThu May 03 09:52:47 2018 -0700 @@ -28,6 +28,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; import java.io.Serializable; /** @@ -1010,6 +1011,85 @@ } /** + * If the specified key is not already associated with a value (or is mapped + * to {@code null}), attempts to compute its value using the given supplier + * and enters it into this map unless {@code null}. + * + * If the supplier returns {@code null}, no mapping is recorded. + * If the supplier itself throws an (unchecked) exception, the + * exception is rethrown, and no mapping is recorded. The most + * common usage is to construct a new object serving as an initial + * mapped value or memoized result that does not depend on the key, + * as in: + * + * {@code + * map.computeIfAbsent(key, () -> new Value()); + * } + * + * Or to implement a multi-value map, {@code Map>}, + * supporting multiple values per key: + * + * {@code + * map.computeIfAbsent(key, HashSet::new).add(v); + * } + * + * The supplier should not modify this map during computation. + * + * @implSpec + * The default implementation is equivalent to the following steps for this + * {@code map}, then returning the current value or {@code null} if now + * absent: + * + * {@code + * if (map.get(key) == null) { + * V newValue = supplier.get(); + * if (newValue != null) + * map.put(key, newValue); + * } + * } + * + * The default implementation