Why Are New Java.Util.Arrays Methods in Java 8 Not Overloaded for All the Primitive Types

Why are new java.util.Arrays methods in Java 8 not overloaded for all the primitive types?

To address the questions as a whole, and not just this particular scenario, I think we all want to know...

Why There's Interface Pollution in Java 8

For instance, in a language like C#, there is a set of predefined function types accepting any number of arguments with an optional return type (Func and Action each one going up to 16 parameters of different types T1, T2, T3, ..., T16), but in the JDK 8 what we have is a set of different functional interfaces, with different names and different method names, and whose abstract methods represent a subset of well known function arities (i.e. nullary, unary, binary, ternary, etc). And then we have an explosion of cases dealing with primitive types, and there are even other scenarios causing an explosion of more functional interfaces.

The Type Erasure Issue

So, in a way, both languages suffer from some form of interface pollution (or delegate pollution in C#). The only difference is that in C# they all have the same name. In Java, unfortunately, due to type erasure, there is no difference between Function<T1,T2> and Function<T1,T2,T3> or Function<T1,T2,T3,...Tn>, so evidently, we couldn't simply name them all the same way and we had to come up with creative names for all possible types of function combinations. For further reference on this, please refer to How we got the generics we have by Brian Goetz.

Don't think the expert group did not struggle with this problem. In the words of Brian Goetz in the lambda mailing list:

[...] As a single example, let's take function types. The lambda
strawman offered at devoxx had function types. I insisted we remove
them, and this made me unpopular. But my objection to function types
was not that I don't like function types -- I love function types --
but that function types fought badly with an existing aspect of the
Java type system, erasure. Erased function types are the worst of
both worlds. So we removed this from the design.

But I am unwilling to say "Java never will have function types"
(though I recognize that Java may never have function types.) I
believe that in order to get to function types, we have to first deal
with erasure. That may, or may not be possible. But in a world of
reified structural types, function types start to make a lot more
sense [...]

An advantage of this approach is that we can define our own interface types with methods accepting as many arguments as we would like, and we could use them to create lambda expressions and method references as we see fit. In other words, we have the power to pollute the world with yet even more new functional interfaces. Also, we can create lambda expressions even for interfaces in earlier versions of the JDK or for earlier versions of our own APIs that defined SAM types like these. And so now we have the power to use Runnable and Callable as functional interfaces.

However, these interfaces become more difficult to memorize since they all have different names and methods.

Still, I am one of those wondering why they didn't solve the problem as in Scala, defining interfaces like Function0, Function1, Function2, ..., FunctionN. Perhaps, the only argument I can come up with against that is that they wanted to maximize the possibilities of defining lambda expressions for interfaces in earlier versions of the APIs as mentioned before.

Lack of Value Types Issue

So, evidently type erasure is one driving force here. But if you are one of those wondering why we also need all these additional functional interfaces with similar names and method signatures and whose only difference is the use of a primitive type, then let me remind you that in Java we also lack of value types like those in a language like C#. This means that the generic types used in our generic classes can only be reference types and not primitive types.

In other words, we can't do this:

List<int> numbers = asList(1,2,3,4,5);

But we can indeed do this:

List<Integer> numbers = asList(1,2,3,4,5);

The second example, though, incurs in the cost of boxing and unboxing of the wrapped objects back and forth from/to primitive types. This can become really expensive in operations dealing with collections of primitive values. So, the expert group decided to create this explosion of interfaces to deal with the different scenarios. To make things "less worse" they decided to only deal with three basic types: int, long and double.

Quoting the words of Brian Goetz in the lambda mailing list:

[...] More generally: the philosophy behind having specialized
primitive streams (e.g., IntStream) is fraught with nasty tradeoffs.
On the one hand, it's lots of ugly code duplication, interface
pollution, etc. On the other hand, any kind of arithmetic on boxed ops
sucks, and having no story for reducing over ints would be terrible.
So we're in a tough corner, and we're trying to not make it worse.

Trick #1 for not making it worse is: we're not doing all eight
primitive types. We're doing int, long, and double; all the others
could be simulated by these. Arguably we could get rid of int too, but
we don't think most Java developers are ready for that. Yes, there
will be calls for Character, and the answer is "stick it in an int."
(Each specialization is projected to ~100K to the JRE footprint.)

Trick #2 is: we're using primitive streams to expose things that are
best done in the primitive domain (sorting, reduction) but not trying
to duplicate everything you can do in the boxed domain. For example,
there's no IntStream.into(), as Aleksey points out. (If there were,
the next question(s) would be "Where is IntCollection? IntArrayList?
IntConcurrentSkipListMap?) The intention is many streams may start as
reference streams and end up as primitive streams, but not vice versa.
That's OK, and that reduces the number of conversions needed (e.g., no
overload of map for int -> T, no specialization of Function for int
-> T, etc.) [...]

We can see that this was a difficult decision for the expert group. I think few would agree that this is elegant, but most of us would most likely agree it was necessary.

For further reference on the subject you may want to read The State of Value Types by John Rose, Brian Goetz, and Guy Steele.

The Checked Exceptions Issue

There was a third driving force that could have made things even worse, and it is the fact that Java supports two types of exceptions: checked and unchecked. The compiler requires that we handle or explicitly declare checked exceptions, but it requires nothing for unchecked ones. So, this creates an interesting problem, because the method signatures of most of the functional interfaces do not declare to throw any exceptions. So, for instance, this is not possible:

Writer out = new StringWriter();
Consumer<String> printer = s -> out.write(s); //oops! compiler error

It cannot be done because the write operation throws a checked exception (i.e. IOException) but the signature of the Consumer method does not declare it throws any exception at all. So, the only solution to this problem would have been to create even more interfaces, some declaring exceptions and some not (or come up with yet another mechanism at the language level for exception transparency. Again, to make things "less worse" the expert group decided to do nothing in this case.

In the words of Brian Goetz in the lambda mailing list:

[...] Yes, you'd have to provide your own exceptional SAMs. But then
lambda conversion would work fine with them.

The EG discussed additional language and library support for this
problem, and in the end felt that this was a bad cost/benefit
tradeoff.

Library-based solutions cause a 2x explosion in SAM types (exceptional
vs not), which interact badly with existing combinatorial explosions
for primitive specialization.

The available language-based solutions were losers from a
complexity/value tradeoff. Though there are some alternative
solutions we are going to continue to explore -- though clearly not
for 8 and probably not for 9 either.

In the meantime, you have the tools to do what you want. I get that
you prefer we provide that last mile for you (and, secondarily, your
request is really a thinly-veiled request for "why don't you just give
up on checked exceptions already"), but I think the current state lets
you get your job done. [...]

So, it's up to us, the developers, to craft yet even more interface explosions to deal with these in a case-by-case basis:

interface IOConsumer<T> {
void accept(T t) throws IOException;
}

static<T> Consumer<T> exceptionWrappingBlock(IOConsumer<T> b) {
return e -> {
try { b.accept(e); }
catch (Exception ex) { throw new RuntimeException(ex); }
};
}

In order to do:

Writer out = new StringWriter();
Consumer<String> printer = exceptionWrappingBlock(s -> out.write(s));

Probably, in the future when we get Support for Value Types in Java and Reification, we will be able to get rid of (or at least no longer need to use anymore) some of these multiple interfaces.

In summary, we can see that the expert group struggled with several design issues. The need, requirement or constraint to keep backward compatibility made things difficult, then we have other important conditions like the lack of value types, type erasure and checked exceptions. If Java had the first and lacked the other two the design of JDK 8 would probably have been different. So, we all must understand that these were difficult problems with lots of tradeoffs and the EG had to draw a line somewhere and make decisions.

java.util.Arrays.toString method is not overloaded for Strings in Scala

It is actually a bug in java that it works. If you look at the list of candidates, you'll see, that clearly there is no suitable alternative. It ends up calling Array[Object] variant by accident, but that is wrong, becuase Array is invariant in its type parameter (all generic types are invariant in java), so Array[Object] is not a superclass of Array[String].

Why have multiple version of Optional in Java 8

It's true that Optional<Integer> behaves quite similar to OptionalInt from a functional point of view. For example, there is no functional difference between the following to methods:

int foo(int value) {
return OptionalInt.of(value).orElse(4242);
}
int bar(int value) {
return Optional.of(value).orElse(4242);
}

However, there can be a difference in performance and efficiency--depending on how the optional types are used and on the capabilities of the JIT compiler. The second method is basically identical to the following method:

int baz(int value) {
return Optional.of(Integer.valueOf(value))
.orElse(Integer.valueOf(4242))
.intValue();
}

As you can see, due to auto-boxing for each method call up to two additional Integer objects will be created. Compared to native types, the creation of an object is expensive, and each additional object increases the pressure on the garbage collection.

That doesn't mean, that it will make a difference for most applications. But it can make a difference, and if it's not there, it lowers the acceptance for Java 8 Streams.

Why are the 'Arrays' class' methods all static in Java?

In Java there is no way to extend the functionally of an array. Arrays all inherit from Object but this gives very little. IMHO This is a deficiency of Java.

Instead, to add functionality for arrays, static utility methods are added to classes like Array and Arrays. These methods are static as they are not instance methods.

Method Overloading Techinques

When the parameter is amulti dimensional array, you can recursively call the function that digs down until you end up with a 1d array of numbers. The logic is:

if a is a multi-dimensional array
for each array in a
call recursively
else
count odd numbers in a

I have 2 functions. One that takes a variable number of args, and a recursive one. The first just calls the second with the var args as an array. The varargs function needs a bit of work if you want to allow mixed parameters (eg: countOdd(new int [] {1,2,3}, 4, 5);)

// The var args version. You call this. It then calls the recursive
// version.
public static <T> int countOdd(T... arguments)
{
return countOddRec(arguments);
}

// Recursive version
private static <T> int countOddRec(T[] a)
{
if (a == null || a.length == 0) return 0;

int count=0;

// Is it an array of Numbers?
if (a[0] instanceof Number) {
for (T i: a) {
// Simplified the counting code a bit. Any # mod 2 is either 0 or 1
count += ((Number)i).intValue() % 2;
}
}
// Is it an multi-dimensional? Call recursively for each sub-array.
else {
for (T sub : a) {
count += countOddRec((T[])sub);
}
}

return count;
}

As mentioned in the comments, this will not work for primitive data types (ex: int, etc). Instead, use non-primitive types (ex: Integer, etc).

How can I make a Java method accept arrays of different (primitive) variable types?

Yes. But the only common ancestral type for all arrays1 is Object. You can use that and java.lang.reflect.Array. Also since printArray has no state, you might as well make it static like

static void printArray(String info, Object pArray) {
StringBuilder sb = new StringBuilder(info);
int len = Array.getLength(pArray);
if (len > 0) {
sb.append("{ ").append(Array.get(pArray, 0));
for (int a = 1; a < len; a++) {
sb.append(", ").append(Array.get(pArray, a));
}
sb.append(" }");
} else {
sb.append("{}");
}
System.out.println(sb);
}

I tested it like

public static void main(String[] args) {
boolean[] bArr = { true, true, false, false };
int[] iArr = { 0, 1, 2, 3 };
printArray("boolean[]", bArr);
printArray("int[]", iArr);
}

Which outputs

boolean[]{ true, true, false, false }
int[]{ 0, 1, 2, 3 }

1Both primitive and reference types. Object obj = new int[] { 1, 2, 3 }; and Object obj = new String[] { "Hello", "World" }; are both valid. All arrays are Objects in Java.

Java-8: boolean primitive array to stream?

Given boolean[] foo use

Stream<Boolean> stream = IntStream.range(0, foo.length)
.mapToObj(idx -> foo[idx]);

Note that every boolean value will be boxed, but it's usually not a big problem as boxing for boolean does not allocate additional memory (just uses one of predefined values - Boolean.TRUE or Boolean.FALSE).

In Java 8, is there a ByteStream class?

No, it does not exist. Actually, it was explicitly not implemented so as not to clutter the Stream API with tons of classes for every primitive type.

Quoting a mail from Brian Goetz in the OpenJDK mailing list:
 

Short answer: no.

It is not worth another 100K+ of JDK footprint each for these forms
which are used almost never. And if we added those, someone would
demand short, float, or boolean.

Put another way, if people insisted we had all the primitive
specializations, we would have no primitive specializations. Which
would be worse than the status quo.

Using Streams with primitives data types and corresponding wrappers

Java 8 stream framework has a generic Stream<T> for objects as elements, and three primitive streams IntStream, LongStream, DoubleStream for the main three primitives. If you work with primitives, use one of those latter, in your case IntStream.

See the picture:

Sample Image

What lies behind is that:

  1. Java generics cannot work with primitive types: it is possible to have only List<Integer> and Stream<Integer>, but not List<int> and Stream<int>

  2. When the Java Collections framework was introduced, it was introduced only for classes, so if you want to have a List of ints, you have to wrap them (each single element!) to Integers. This is costly!

  3. When the Java Streams framework was introduced, they decided to get around this overhead and in parallel with the "class-oriented" streams (using the generics mechanism), they introduced three extra sets of all the library functions, specifically designed for the most important primitive types: int, long, double.

BTW, they did the same with the predefined functional interfaces in the java.util.function package, so you have Predicate, IntPredicate, DoublePredicate, and LongPredicate.

And see also a marvelous explanation here: https://stackoverflow.com/a/22919112/2886891

java function that work with objects and primitives

Sadly generics won't work here as generics do not cover primitives but they do cover primitive arrays. Primitive arrays are also not autoboxed into a wrapper class array. That is something you would have to do manually, but that is very very slow. One solution is to provide an override for each primitive, similar to how Arrays.sort is implemented. This creates a lot of duplicate code. Another solution is to use an Array interface similar to ArrayList that emulates an Array, get/set methods for indices, and provide subclasses for each primitive and one for objects. You can then use the Array interface and not care what the actual type is.



Related Topics



Leave a reply



Submit