The SAM approach is actually somewhat similar to what Scala (and C++11) do with anonymous functions (created with Scala's =>
operator or C++11's []()
(lambda) syntax).
The first question to answer on the Java side is whether to have the return type of a lambda statement be a new primitve type, like int
or byte
, or an object type of some sort. In Scala, there are no primitive types -- even an integer is an object of class Int
-- and functions are no different, being objects of class Function1
, Function2
, and so forth, depending on the number of arguments the function takes.
C++11, ruby and python similarly have lambda expressions which return an object which is callable in explicit or implicit way. The returned object has some standard method (such as #call
which can be used to call it as a function. C++11, for instance, uses a std::function
type which overloads operator()
so that calls of the object's call method even look like function calls, textually. :-)
Where the new proposal for Java gets messy is in the use of structural typing to allow assigning such a method to another object, such as a Comparator
which has a single main method with a different name. While this is conceptually icky in some ways, it does mean that the resulting object can be passed to existing functions which take an object to represent a callback, a comparator, and so on, and expect to be able to call a well-defined single method such as #compare
or #callback
. C++'s trick of overriding operator()
neatly dodged this issue even before C++11, since all such callback options were callable the same way, and thus the STL required no adjustment to allow C++11 lambdas to be used with sort
and so on. Java, not having used a standard naming for such objects in the past (perhaps because no trick like operator overloading made a single approach obvious), isn't so lucky, so this hack prevents them from having to change a whole lot of existing APIs.
Because immutable collections absolutely require sharing to be usable. Otherwise, every single operation drops a whole other list into the heap somewhere. Languages that are entirely immutable, like Haskell, generate astonishing amounts of garbage without aggressive optimizations and sharing. Having collection that's only usable with <50 elements is not worth putting in the standard library.
Further more, immutable collections often have fundamentally different implementations than their mutable counterparts. Consider for example ArrayList
, an efficient immutable ArrayList
wouldn't be an array at all! It should be implemented with a balanced tree with a large branching factor, Clojure uses 32 IIRC. Making mutable collections be "immutable" by just adding a functional update is a performance bug just as much as a memory leak is.
Furthermore, sharing isn't viable in Java. Java provides too many unrestricted hooks to mutability and reference equality to make sharing "just an optimization". It'd probably irk you a bit if you could modify an element in a list, and realize you just modified an element in the other 20 versions of that list you had.
This also rules out huge classes of very vital optimizations for efficient immutability, sharing, stream fusion, you name it, mutability breaks it. (That'd make a good slogan for FP evangelists)
Best Answer
While there can be exactly one max value in a collection, there can be more than item representing that value. E.g
{1, 9, 2, 9, 0}
has max value of9
, represented by both elements[1]
and[3]
.Note that not all collections support index access; e.g. a
Set<Integer>
can have a meaningful maximum but accessing an element by index makes no sense in it.Even if we limit the method to
List
, it would be a bit hard to come up with one method to find indices of the maximum value that is not clumsy. You could return a list of indices, but then you'd lose the value, and in some collections, e.g. linked lists, accessing an element by index is slow. Since Java does not have an easy syntax for tuples, you'd have to return a special type of object with.getValue()
and.getIndices()
.But I think that such an operation is just not common enough to be supported in the standard library. Finding a maximum is literally 3-4 lines of code, and tracking the index is another 1-2 lines, and there's no much room to do it wrong. If you do it a lot, you can easily put it in your own utility class.