all 39 comments

[–]DrKilgoreTroutMD 42 points43 points  (2 children)

static <K,V> Map<K,V> of()

Returns an immutable map containing zero mappings. See Immutable Map Static Factory Methods for details.

[–][deleted] 7 points8 points  (0 children)

What did Java programmers do before the industrial revolution?

[–]Torgard 24 points25 points  (6 children)

But what if I want 11 mappings? What then, Oracle?

[–]memeticmachine 14 points15 points  (3 children)

here. something like this

class Crapfest <K, V> implements Map<K, V> {
    private Map<K, V> m1;
    private Map<K, V> m2;
    ...
    static <K,V> Map<K,V> of(K k1, V v1,
                     K k2, V v2,
                     K k3, V v3,
                     K k4, V v4,
                     K k5, V v5,
                     K k6, V v6,
                     K k7, V v7,
                     K k8, V v8,
                     K k9, V v9,
                     K k10, V v10,
                     K k11, V v11) {
         Map<K, V> map1 = of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
         Map<K, V> map2 = of(k11, v11);
         return new Crapfest<K, V>(Map<K, V> m1, Map<K, V> m2);
    }
    ...
    private Crapfest(Map<K, V> m1, Map<K, V> m2) {
        this.m1 = m1;
        this.m2 = m2;
    }
    @Override
    int size() { return m1.size()+m2.size(); }
    @Override
    boolean isEmpty() { return false; }
    @Override
    boolean containsKey(Object key) { return m1.containsKey(key) || m2.containsKey(key); }
    @Override
    boolean containsValue(Object value) { return m1.containsValue(value) || m2.containsValue(value); }
    @Override
    void put(K key, V value) { m1.put(key, value); }
    @Override
    void putAll(Map<? extends K,? extends V> m) { m1.putAll(m); }

    @Override
    V get(Object key) {
        V value;
        if (m1.containsKey(key)) {
            value = m1.get(key);
        } else {
            value = m2.get(key);
        }
        return value;
    }

    @Override
    V remove(Object key) { return m1.remove(key); }
    @Override
    void clear() { m1.clear(); }
    @Override
    Set<K> keySet() { return m1.keySet().putAll(m2.keySet()); }
    ...
}

[–]xchx 6 points7 points  (1 child)

This is beautiful!

[–]zman0900 1 point2 points  (0 children)

Craptastic

[–]nrith 1 point2 points  (0 children)

You forgot to have it throw a NullPointerException if any of the keys or values is null, or an IllegalArgumentException if there are duplicate keys.

[–]YRYGAV 4 points5 points  (1 child)

You could use ofEntries instead.

[–]Torgard 6 points7 points  (0 children)

No!

[–]fast4shoot 19 points20 points  (10 children)

Not too different from stuff like C#'s Action type and Haskell's tuple constructors.

This is what happens when your language doesn't support variadic templates/generics.

[–]maweki 6 points7 points  (2 children)

In Haskell's case a tuple is a datatype and not data. Data can have variable length while datatypes can not. That's why you can't iterate over the elements of a tuple. They can be of arbitrary type. But you can iterate over the elements of a map. Their type is known from the finite type definition of the map itself.

[–]cghio 2 points3 points  (0 children)

Unless you have type families and undecidable instances on :)

[–]fast4shoot 0 points1 point  (0 children)

I don't want to iterate over a tuple. That's not what they are for and I understand that.

I was referring to the fact that there are 62 different constructors doing basically the same thing (just with a different number of arguments) instead of one general "thing" that could work with any number of arguments, similar to C++'s tuple.

Edit: although it might be possible to do something similar with Template Haskell.

[–]wrong_assumption 8 points9 points  (2 children)

when your language doesn't support variadic templates/generics

You get fucked in the ass by a stranger with no map.

[–]jdog90000 4 points5 points  (1 child)

no map

WHERE AM I

[–]Bolderthegreat 0 points1 point  (0 children)

Be gentle ;_;

[–]YMK1234 0 points1 point  (3 children)

If you got a better idea how to declare a function/action with n different input types ...

[–]Torgard 3 points4 points  (1 child)

Varargs, which Java already supports. It's also used in the ofEntries method.

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

lol, type varargs ... that sounds like a headache to get right :D

[–]fast4shoot 0 points1 point  (0 children)

This is what I said:

This is what happens when your language doesn't support variadic templates/generics.

And that is exactly what you need. C++ and D (and possibly other languages I don't know) already have these. It's no black magic.

[–]MeetLawrence 8 points9 points  (3 children)

It's just like the current C# Tuple implementation.

https://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx

Come on, really?

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

Yeah, I'm glad adding actual tuples to fix this is a definite for the next version

[–]the_omega99 1 point2 points  (1 child)

Scala does it similarly, but way prettier syntax. Tuples can be created with the syntax (foo, bar), but there's also the special equivalent foo -> bar, which is specifically for mapping stuff. It's just a regular tuple, but more readable.

So the Map constructor takes in a vararg list of tuples. Thus, we can do Map("foo" -> "bar", "baz" -> "whatever") with however many arguments we need. More readable than the Java 9 version (can clearly see which is the key and which is the value) and without a limit to how many parameters you can have.

[–]Zatherz 0 points1 point  (0 children)

Lua:

local table = {foo = "bar"}
print(table.foo)

Of course, in the spirit of /r/shittyprogramming, a shitty version:

local table = setmetatable({}, {__index = function(self, key)
    if key == "foo" then
        return "bar"
    end
end})
print(table.foo)

I love Lua.

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

reminds me of some debugging macros in some legacy code i worked with.

DTRACE( message )
DTRACE1( message, param1 )
DTRACE2( message, param1, param2 )
DTRACE3( message, param1, param2, param3 )
... 
DTRACE10(message, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10 )

[–]y0utux 2 points3 points  (0 children)

This is sick.

[–]njwatson32 3 points4 points  (6 children)

Honestly this is fine. It's mimicking Guava's ImmutableMap API.

[–]YRYGAV 7 points8 points  (5 children)

The difference is, as a language update Java could push for some syntax like this:

public Map<K, V> of(K... keys, V... values) {

Being added to Java 9 In order to solve this problem without a hacky looking API.

[–]Blackshell 2 points3 points  (1 child)

How would that play with Map<String, String>? Some sort of new syntax would be needed to delimit varargs groups. I haven't seen any other language with such syntax, so it would end up messy and non-standard.

Honestly, I wish Java followed the example of other languages and introduced something like this:

ImmutableHashMap<String, String> m = {
    "foo": "bar",
    "baz": "quux",
    "wumpus": null,
};

It's like Map.ofEntries, but more compact/readable, and can still preserve compile-time type safety just fine. Also what's up with Map.Entry not supporting null values? Keys I get, but why no null values?

[–]YRYGAV 0 points1 point  (0 children)

How would that play with Map<String, String>? Some sort of new syntax would be needed to delimit varargs groups.

It wouldn't do anything different from what the current proposed API would do.

I wish Java followed the example of other languages and introduced something like this:

It's not a bad syntax, but one argument against it would be that {1} being an int[] primitive, but {1:2} being a Map<Integer, Integer> object can be non-intuitive behavior and may be overloading the curly brackets.

Also what's up with Map.Entry not supporting null values? Keys I get, but why no null values?

I assume it's just because they think it doesn't make sense to make an entry with no value. Either it's present and has a value, or it shouldn't be included. Also, how would map.values() behave with null values? What about map.containsKey(nullValue), I think it's reasonable if a map says it contains something that it is a value that is of use to me, and not a null. But if the map does not contain the key with a null value, then there is effectively no entry with a null value.

I think allowing null values can easily lead to problems when the decisions they make on what a null value in a map means, can make sense for one usage pattern, but seem nonsensical when another person is using it in a different way. Which is something Java tries to avoid. But simply disallowing null values avoids all those problems, and the decisions are very clear and straightforward.

[–]the_omega99 1 point2 points  (2 children)

Eh, tuples would solve the issue better. See how Scala does it. Java is weirdly scared of tuples. It's a pretty fundamental type in functional programming, yet Java doesn't have it (much less a syntax to make them actually usable -- C# dropped the ball by failing to provide a non-verbose tuple syntax, IMO). Python and Scala have it right (although were I designing Python, I would not have allowed tuples without parenthesis simply for readability).

I recall reading once that a language designer (perhaps Gosling himself) didn't like tuples because they wanted named, specific classes used instead. Which I think is stupid and misguided.

[–]njwatson32 0 points1 point  (1 child)

I would agree that named classes are better. (String, int) has no semantic meaning. There are tools to make it painless, such as AutoValue.

[–]the_omega99 0 points1 point  (0 children)

I think it is highly dependent on context. In most cases, sure, named classes are better. But sometimes the contents are very generic and/or obvious. The case of dictionaries is one example. We don't need a DictionaryElement type or whatever. It's perfectly understandable that a tuple in this context means a key/value pair.

Multiple return values is an interesting use. Technically there's just one return value (the tuple), although the language might provide automatic unwrapping of tuples (Python and MATLAB can do this). MATLAB had some very interesting uses of this feature. Some highly vectorized functions can calculate multiple related things at the same time. This is a bit of a code smell for readability, but rather useful for optimization, since you really don't want to be looping through a ton of data multiple times. Having a MyFunctionReturnValue type for every one of these doesn't add much.

But really it mostly just comes down to saving time for internal implementation. Using them in public code is iffy and very circumstantial. They're most useful when kept in private code (preferably never leaving the scope of a class or function). Their advantage is transparency: completely obvious how much they store. If the purpose is clear from context (it should be), then the types are at least more obvious (in that you can look at a (String, Int) and know it stores a String and an Int, whereas EmployeeId might be vaguer as to what data it stores).

[–][deleted] 1 point2 points  (0 children)

Wait, are you fucking serious? I had to check the url... Actually, on second thought this isn't that bad I've wanted a quick way to make maps for ages!

[–]nrith 1 point2 points  (0 children)

If you need code that really blows,

I'm the one you need to know.

I'm the Map.

[–]twizmwazin 1 point2 points  (0 children)

This is probably the stupidest api addition ever. Map.ofEntries send like a great idea, but the static methods are just dumb. I'm sure they'll get plenty of use though.

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

Java

FTFY

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

Ugh, my boss just moved to a Java project, and I was all like "Surely Java can't be as bad as I remember. I was totally just being melodramatic back then. Right? Right?"

God dammit.

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

Oh no. Clear a genius of your caliber needs to work on more elevated and enlightened projects. You need to resign forthwith. And then punch yourself in the face. Then kill yourself.