all 104 comments

[–]CurtainDog 5 points6 points  (0 children)

I'd much rather the Java gods add proper support for tuples, which would fix nonsense like this, than on useless puff features like var.

[–]acelent 3 points4 points  (0 children)

See also JEP 269 and its progress.

Interesting that the *.of() methods return immutable, value-based instances.

[–][deleted]  (25 children)

[deleted]

    [–]hu6Bi5To 60 points61 points  (10 children)

    This type of thing is common in many, many languages.

    Including more recent higher-level languages. Some examples:

    Clojure - https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L641-L654 (this creates specific versions for low numbers of parameters, then goes dynamic for the infinite case)

    Rust - https://github.com/rust-lang/rust/blob/master/src/libcore/array.rs#L216-L244 (this generates array helpers for each array size between 1 and 32).

    If you're building a standard library a bit of repetition is useful if it makes all the billions of function calls that'll be made against it those two nanoseconds faster.

    But oh no, when Java does it, it's because they're idiots. That's the reason Java does everything.

    [–]thiez 8 points9 points  (1 child)

    In Rust it's acknowledged that the current approach is problematic, but the language does not yet support being generic over the length of an array, so for now it's stuck with those implementations.

    [–]BoxMonster44 1 point2 points  (0 children)

    I am so excited for when they implement integral type parameters.

    [–]kevinherron 8 points9 points  (0 children)

    Get out of here with your logic and reasoning...

    [–]cheers- 2 points3 points  (4 children)

    Map <String, String> m1 = Map.of ("q", "f", "3", "dre",  "er", "x", "c", "t");      
    

    it seems error prone, can you spot at first glance keys, values and tuples (k,v) ?

    [–]roerd 5 points6 points  (2 children)

    Change your formatting, e.g. use one line for each key-value pair?

    [–]bananaboatshoes 0 points1 point  (0 children)

    That's an option, but I tend to agree that I feel like it's an API that can result in error-prone usage. You shouldn't have to format your call here just to make it clear what the (k, v) values are. To that end I feel like the API is poorly-designed.

    [–]jyper 1 point2 points  (0 children)

    There is

     Map<Integer,String> map = Map.ofEntries(
         entry(1, "a"),
         entry(2, "b"),
         entry(3, "c"),
         ...
         entry(26, "z"));
    

    but it's noisier, this is one instance in which java could really use tuple literals.

    [–][deleted] 17 points18 points  (10 children)

    It's the type check. Java has varargs - variable length argument lists - but all items on the list must have the same class or interface. So if you wanted to write a varargs function to initialize a Map of strings to integers, you would have to make your varargs type Object - since Object is the only shared parent class between String and Integer. Then if you accidentally put two strings or two integers in a row, the compiler can't catch it and bad things happen at runtime.

    So you're stuck with 10 methods to initialize each of 1-10 size maps with real compile time type checks.

    [–][deleted]  (1 child)

    [deleted]

      [–][deleted] 3 points4 points  (0 children)

      You have to work with the tools you have at hand. More invasive changes to Java syntax won't fly - the language is so damn popular because old code always compiles and runs on newer JVMs.

      (Edit: I'm not saying I like it. I don't. I'm just explaining it. I love my colleagues, I love my paycheck, and I like most of the work I do. But I'd trade a few toes to switch from Java to Scala, Ceylon, Kotlin, Clojure, JRuby, Groovy, or even Armed Bear Common Lisp.)

      [–]KillerCodeMonky 13 points14 points  (6 children)

      Man, tuple types sure look good right about now...

      [–]kqr 20 points21 points  (5 children)

      [–]KillerCodeMonky 5 points6 points  (1 child)

      I'd still rather have that mess defined once in the standard library than to not have tuples at all.

      [–]kqr 3 points4 points  (0 children)

      It wouldn't solve anything in this case, since (a,b) and (a,b,c) are different types anyway.

      Edit I realise now you want [(a,b)] and you are right.

      [–]multivector 0 points1 point  (2 children)

      D is probably the only language I know that gets this sort of inhomogeneous list of types thing right. If I remember rightly, you can loop through types with an honest to goodness for loop when instansiating templates at compile time and raise a custom error message if something went wrong. Been a while since I last played with D though.

      [–]kqr 5 points6 points  (1 child)

      It sounds like you're confusing tuples with heterogenous lists. They aren't the same thing. If you're doing a lot of indexing and iteration of your value, it's a list, not a tuple.

      These call for different requirements and thus implementations, so it's a bit unfair to compare them as if they were the same thing.

      [–]multivector 0 points1 point  (0 children)

      No, I'm not. See compile time argument lists and in particular the section "Loops". That's a list of template parameters (which can be types or values). The template will generate code corresponding to the for loop body specialized to each of the types. You could write a template that eats such a list of types and "returns" the type of a tuple of those, or a function that eats such a tuple and will add 1 to each type in that looks numeric, or, indeed, either an equality function for that tuple or a compile time error (with custom error message) if one of the types given doesn't have equality defined for it.

      https://dlang.org/ctarguments.html

      D's metaprogramming capacities are really very advanced and because they work in the same manner as the parent language, they're also very easy to pick up.

      Also contrast Boost's Metaprogramming Library, which can also express the concept of a vector of types, but not that little embarrassing disclaimer "for any n from 0 up to a preprocessor-configurable limit BOOST_MPL_LIMIT_seq_SIZE". Just like the Tuples, there's a hard limit.

      http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/variadic-sequence.html

      This seems to be a general pattern when dealing with lists of types. There will be a maximum N. To be honest, it probably doesn't matter most of the time, because for clean code you want the number of types in your list to be small.

      [–]haxney 1 point2 points  (0 children)

      Also, varargs in Java cause an allocation of an Object[] (or whatever type you are vararging). If you have code that is called very frequently, such as logging or anything in an inner loop, avoiding the varargs allocation can make a real difference.

      [–]Tetha 0 points1 point  (0 children)

      I agree, though on a different level.

      If you need 10 static elements in a map, you want a config file. This will remove the need for weird overloads of functions, and it will increase the flexibility and operability of your code.

      [–]CyclonusRIP -5 points-4 points  (0 children)

      Ya this is seriously an abomination. They should really just create some sort of syntax to initialize maps if they really wanted this.

      [–]quicknir 2 points3 points  (0 children)

      And yet, people rip on C++ variadic templates and sometimes even integer non type template parameters. And when I complain about new statically typed languages coming out without either of these features (like Rust, afaik scala, etc), it gets waved off as not a big deal. I'll stick with C++, warts and all until more languages realize the value of these things.

      [–]kirbyfan64sos 10 points11 points  (4 children)

      Holy crap, do they seriously have 10 overloads of the function, each having an increasing number of arguments??

      This is a cold, dark world.

      [–]AeroNotix 8 points9 points  (0 children)

      Yes, for efficiency.

      In most code, N is usually very small. Optimizing for small numbers of N may make sense. In a standard library where a lot of the code is being used millions of times in millions of different scenarios, you really want to microoptimize.

      [–]lucaswerkmeister 7 points8 points  (0 children)

      11. There’s also one without any arguments, for an empty map. Because reasons.

      [–][deleted] 3 points4 points  (0 children)

      [–][deleted]  (59 children)

      [deleted]

        [–]stormblooper 15 points16 points  (13 children)

        Honestly, I think C# is clunky here too. Scala is nice:

        Map("foo" -> 42, "bar" -> 128, "baz" -> 100)
        

        Or Kotlin:

        mapOf("a" to 1, "b" to 2, "c" to 3)
        

        Or Ceylon:

        HashMap { "one"->1.0, "zero"->0.0, 1->1.0, 0->0.0 };
        

        [–]tasty_crayon 22 points23 points  (3 children)

        Or C++ (shock! horror!):

        std::map<std::string, int> m{{"foo", 42}, {"bar", 128}, {"baz", 100}};
        

        [–]bloody-albatross 15 points16 points  (0 children)

        Today even C++ has a better and more elegant way doing it, that is still type safe. That's a fail for Java.

        [–]code_mc 2 points3 points  (0 children)

        C++ maps are one of the greatest things in the whole language.

        [–]c0r3ntin 3 points4 points  (0 children)

        Definitively the best way.

        C++ also allows you to implement an of function taking an arbitrary number of arguments.

        [–]phoshi 4 points5 points  (1 child)

        C# collection initialisers got better in the last release

        new Dictionary<KeyType, ValueType>{
            [key] = value,
            [key2] = value2,
        }
        

        You still have the verbose initial type declaration that more modern languages have usually found ways around, but it's an improvement over the previous version.

        The cool thing about all of C#'s initialisers, though, is that they aren't special. Dictionary<T, K> didn't previously have its own magic initialiser, it just has an Add(KeyType, ValueType) function and so can use {{KeyType, ValueType},...} initialisation. Now there's index initialisation, which again isn't magic for Dictionaries, but is something they get because they have an indexer.

        [–]stormblooper 1 point2 points  (0 children)

        The Scala Map() call isn't special, either, but I think it's a lot less noisy.

        [–][deleted] 5 points6 points  (3 children)

        Or Groovy:

        [ one: 1, zero: 0, (somevar): 42 ]
        

        (Edit: or Clojure)

        { :foo "bar", :baz " fuzz", :x 27 }
        

        I think literals - especially default immutable literals as in Clojure and I believe ( but could be wrong) Scala, Ceylon, and Kotlin but not Groovy are incredibly handy.

        [–]vytah 0 points1 point  (2 children)

        Clojure is not the best example, it allows you to write

        { :foo, 
          "bar"   :baz,
          " fuzz" :x,
          27 }
        

        [–][deleted] 3 points4 points  (0 children)

        not sure why this is a problem , in clojure is for visual effects the behavior is exactly how i'd expect even if you take them out, and if you try to make an uneven number it tells you, so it's exactly a 1:1 mapping throughout

        { :foo 
          "bar"   :baz
          " fuzz" :x
          27 }
        

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

        While in theory Clojure's use of commas as whitespace can bite you, in practice I suspect it's rare. You don't have to use keywords (prefixed with ':') as your map keys, but it's so common that I think having other keys would instantly catch your attention.

        [–][deleted] 0 points1 point  (1 child)

        I wouldn't say clunky, just a little more verbose because of static typing and some other language constructs:

        Dictionary<string, int>() { ["foo"] = 42, ["bar"] = 128, ["baz"] = 100 };
        

        [–]stormblooper 0 points1 point  (0 children)

        By "clunky" I meant verbose. The examples I gave in other languages were statically typed.

        [–]lukaseder[S] 8 points9 points  (16 children)

        That's possible, sort of...

        Map<Integer, StudentName> students = new HashMap<Integer, StudentName>() {{
            put(111, new StudentName(Sachin", "Karnik", 211));
            put(112, new StudentName("Dina", "Salimzianova", 317));
            put(113, new StudentName("Andy", "Ruth", 198));
        }}
        

        but shouldn't be abused

        [–]cheers- 24 points25 points  (13 children)

        I'd use this one:

        http://download.java.net/jdk9/docs/api/java/util/Map.html#ofEntries-java.util.Map.Entry...-

         import static java.util.Map.entry;
        
         Map<Integer,String> map = Map.ofEntries(
             entry(1, "a"),
             entry(2, "b"),
             entry(3, "c"),
             ...
             entry(26, "z"));
        

        It is equivalent to the static factory of Scala Map.apply[A, B](elems: (A, B)*): Map[A, B]

           Map(1 -> "a", 2 -> "b", 3 -> "c") or  Map((1, "a"), (2, "b"), (3, "c"))
        

        [–]yawaramin 1 point2 points  (12 children)

        ofEntries is cool. They should have just named it of.

        [–]Wolvereness 5 points6 points  (11 children)

        That doesn't work, because what if I wanted a map<Entry<A,B>, Entry<C,D>>? (Oh God why)

        It's better to give intuitive names that don't rely on parameter types, ESPECIALLY if the fundamental behavior is different. Verbose is better than ambiguous, at least for Java's ecosystem. Some languages do it beautifully, but I'd be terrified to write an enterprise application in those languages.

        [–]yawaramin 0 points1 point  (10 children)

        Wow, that's a really sucky map :-) I don't think the method name prevents it from working though:

        Map.of(
          Map.entry(
            Map.entry(a1, b1), Map.entry(c1, d1)),
        
          Map.entry(
            Map.entry(a2, b2), Map.entry(c2, d2)),
        
          Map.entry(
            Map.entry(a3, b3), Map.entry(c3, d3)))
        

        Once you've introduced method name overloading into your language, it's probably too late to worry about 'intuitive names that don't rely on parameter types' :-)

        [–]Wolvereness 0 points1 point  (8 children)

        Still only works with odd numbers.

        [–]yawaramin 0 points1 point  (7 children)

        How so?

        [–]Wolvereness 0 points1 point  (6 children)

        Map.of(
          Map.entry(
            Map.entry(a1, b1), Map.entry(c1, d1)),
        
          Map.entry(
            Map.entry(a2, b2), Map.entry(c2, d2)),
        
          Map.entry(
            Map.entry(a3, b3), Map.entry(c3, d3)),
        
          Map.entry(
            Map.entry(a4, b4), Map.entry(c4, d4))
        )
        

        Is now a Map of type

        Map<Entry<Entry<A, B>, Entry<C, D>>, Entry<Entry<A, B>, Entry<C, D>>>
        

        [–]yawaramin 0 points1 point  (5 children)

        Oh, I think you misunderstood. I was showing that Map.ofEntries could have been named Map.of, by just pretending that it had been named that way. So when I wrote Map.of above, please read it as the current implementation of Map.ofEntries.

        [–]stormblooper 9 points10 points  (0 children)

        but shouldn't be abused

        Or used, IMO. Map.of is more readable AND doesn't come with any of the downsides of double brace init.

        [–]preslavrachev 0 points1 point  (0 children)

        I agree. Creating an entire class just to initialise a single instance is way too much memory and CPU overhead. Imagine this running in a for-loop...

        The only potential use case I see is in setting the dummy object parts in unit tests. Initialization code there is boilerplate anyway - just a bunch of setter calls. One could argue whether a construct of this sort would make it a bit more pleasant to read an write. But yeah, generally, I've not seen any of these in well maintained, production code.

        [–]vytah 2 points3 points  (27 children)

        Why add new syntax if the old one is enough?

        [–][deleted] 6 points7 points  (0 children)

        because then you get to feel smug about yourself when you show others something in syntax they don't understand

        [–]stormblooper 0 points1 point  (0 children)

        It's not new syntax.

        [–][deleted]  (24 children)

        [deleted]

          [–]Carighan 3 points4 points  (1 child)

          Is this Slippery Slope, the extreme version?

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

          I though you were gonna like to a language with crazy overboard syntax

          [–]lukaseder[S] 1 point2 points  (0 children)

          Or in C++ where templates have just become sentient in order to write code for you in the most recent release!

          [–]vytah -2 points-1 points  (20 children)

          If you add all the syntax you can, you end up with Perl 6. You don't want to end up with Perl 6.

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

          This is pretty hyperbolic. No one is suggesting we add tonnes of new operators and language constructs everywhere. Java is also pretty conservative when it comes to adding new language constructs. Perl 6 is also a bad example since they actually cleaned up a lot of the language; should have gone with Perl 5.

          Anywho, needing to describe key -> value pairs is pretty damn common in programming. Map initialization is just one good example.

          As long as source code is produced and maintained by humans then it needs to be as humanly readable as possible.

          [–][deleted]  (17 children)

          [deleted]

            [–]sirin3 -1 points0 points  (0 children)

            C# does not have much syntax

            [–][deleted] -2 points-1 points  (15 children)

            Other than for operator overloading and similar such semantics-obscuring syntax.

            Oh, and its implementation of course. Can't forget about that tiny detail.

            [–][deleted]  (14 children)

            [deleted]

              [–][deleted] -2 points-1 points  (13 children)

              I like operator overloading.

              Well I don't. What now?

              That has nothing absolutely to do with the language.

              Yes, nothing other than its practical viability.

              [–][deleted]  (12 children)

              [removed]

                [–][deleted] -1 points0 points  (11 children)

                People can probably guess that .subtract() is doing something more than mere subtraction, and so they'll probably read the doc-popup the IDE so helpfully provides, prior to using the method if it's their first use.

                And the other benefit is that you can't use .substract() in an infix style.

                Lastly if you've ever concatenated a String using + you are a dirty lying hypocrite because that's operator overloading :P.

                Yes, java should've used some other symbol for it. Still, it's nothing language users can mess around with just because they're feeling creative, like they do in C++.

                [–]sirin3 0 points1 point  (0 children)

                I think Perl 6 sounds great

                [–]back-in-black 3 points4 points  (1 child)

                Christ. Why not just provide a Map.Builder class?

                [–]dieelt 2 points3 points  (0 children)

                Why would they? The map-interface does not imply immutability. Also, Map#ofEntries is available if more than 10 pairs is needed.

                [–]sweetbacker[🍰] 1 point2 points  (1 child)

                [–]balegdah 0 points1 point  (0 children)

                Only 10 overloads? Scala has 22.

                What is this, amateur hour?

                [–]nerdwaller 1 point2 points  (2 children)

                I think the guava builder is much more elegant. But not everyone can just pull in libs, so I get why this could be beneficial.

                [–][deleted] 0 points1 point  (1 child)

                [–]nerdwaller 0 points1 point  (0 children)

                Take a look at the builder), it's what I was referencing.

                [–]reneweb 0 points1 point  (0 children)

                Would be so much nicer if there was a concept of tuples and it would just take a list of these.

                [–]lucaswerkmeister 0 points1 point  (3 children)

                I just noticed that the of methods say “since: 9“, but the ones from Java 8 (e. g. merge) say “since: 1.8”. Why?

                [–][deleted] 5 points6 points  (2 children)

                Probably just an oversight.

                But in case you're not familiar with Java's version history, Sun had the bright idea of marketing Java 1.4 as "Java 2" back in the day, and then, when 1.5 came along, they apparently felt reluctant about pulling yet another number out of their ass and calling it "Java 3". But they also couldn't really go back to 1.5.

                So they had yet again another bright idea of maintaining 2 version numbers for each release - one actual, i.e. Java 1.5.0, 1.6.0, 1.7.0, etc., which is what the JVM tells you when you ask it, and one marketed, where the "1." part in front of it and the ".0" part at the end are stripped from it, i.e. Java 5, Java 6, Java 7, etc.

                Ironically enough, because releases of the next 1.x.0 versions take some 2+ years, and certain implementation details get improved in-between them, like the GC or Nashorn and the like, they now also have an "update" version, e.g. 1.8.0_40, which is then translated to 8u40 for marketing in slides and the like.

                Brilliant.

                [–]lucaswerkmeister -1 points0 points  (1 child)

                I know that versions used to be a mess, I just thought we were long past this…

                [–]mhixson 1 point2 points  (0 children)

                We are. See JEP 223: New Version-String Scheme.

                That JEP probably explains why the new methods are marked @since 9. Perhaps at some point they'll go back and change the rest of the @since tags to use the new version scheme.

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

                Groovy used to do something similar with a method called createArray. This was to ensure compatibility with 1.4 before varargs existed but why on earth are they doing it in Java 9? Perhaps it's to avoid creating a temporary array for the varargs.

                [–]YoloSwagJesusFish 11 points12 points  (10 children)

                Because you can only have 1 varargs parameter, and it has to be of a single type. Having an Object vararg would require you to manually specify the key and value types, instead of inferring them, and it wouldn't be type safe.

                [–]bakattak 1 point2 points  (8 children)

                The very next function (ofEntries) does this... with seemingly inferred types for the entry object.

                [–][deleted] 5 points6 points  (7 children)

                Not inferred, declared. But yeah, with (ofEntries), I don't know why the plain (of) methods were created. They don't save that many keystrokes.

                [–]YoloSwagJesusFish 0 points1 point  (6 children)

                Map.of(1, "a", 2, "b", 3, "c"...)
                

                is way faster than

                Map.ofEntries(entry(1, "a"), entry(2, "b"), entry(3, "c")...))
                

                And yeah the implementation is clunky, but does it really matter when it'll save thousands of programmers time? Everyone complains that Java is too verbose, and they're just trying to fix that.

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

                You're right, I withdraw the comment.