Strange Arrays.asList() behavior - java

I have this method:
//not related to the error, but included for reference
ArrayList<ArrayList<Color>> a = new ArrayList<ArrayList<Color>>();
void addColorToList(float[] j) //this array always length 3
{
float[] k = Arrays.copyOf(j, 3);
Arrays.sort(k);
//Error in the following line
a.get(Arrays.asList(j).indexOf(k[0])).add(new Color(j[0], j[1], j[2]));
}
and this error:
Exception in thread "AWT-EventQueue-1"
java.lang.ArrayIndexOutOfBoundsException: -1
I've determined that my code always calls a.get() with -1, because Arrays.asList(j). indexOf(k[0]) does not find the element. However, I cannot figure out why this doesn't work as I would expect it to. I tried printing out the result of Arrays.asList(j), but I'm not really sure what to make of the result: [[F#307af497]. Can anybody tell me what the issue is?

Lets start with this:
I tried printing out the result of Arrays.asList(j), but I'm not really sure what to make of the result: [[F#307af497].
You are printing a list using (effectively) the toString() method. So ...
The outer '[' and ']' are from the list formatting.
You have a list consisting of one element.
That element is an array of float. (The [F#307af497 is produced by Object.toString(), and [F is how the type float[] is rendered ...)
This is actually an important clue. Arrays.asList(float[]) is returning a "list of float[]" ...
But why?
Well, because that's what it is supposed to do!!
The Arrays.asList signature is Arrays.asList<T>(T ...). That means it expects either an explicit T[] ... or a sequence of zero or more T instances, which it will then wrap as an Object[]. The Object[] is then wrapped as a List<Object> (roughly speaking).
The critical thing here is that the actual type T must be a reference type ... not a primitive type.
But your code seems to be expecting an overloaded method like this Arrays.asList(float ...) ... and expecting that that will give you your float[] wrapped as a List<Float>.
Unfortunately, there is no such overload for asList.
So what is actually happening is that:
your call is binding to Arrays.asList<float[]>(float[] ...)
the varargs is causing j to be wrapped in an array; i.e. equivalent to new float[][]{j}
the result is an instance of List<float[]> ... with one element.
So what is the solution?
In general ...
One solution would be to represent your floats as a Float[] rather than a float[]. Ideally, you would push this change back through the code that created the array in the first place, etcetera.
A second solution would be to convert the float[] to a Float[] before calling asList. The simple way to do that is with a simple loop. (There may also be a 3rd-party library for doing this.) The downsides are:
the conversion needs to happen each time you call this method which could be expensive if you call it a lot, and
there is no connection between the original array and the array that you have wrapped ... if you wanted to update the array through the list wrapper.
But in this case, the best solution is to simply replace this:
Arrays.asList(j).indexOf(k[0])
with a simple loop that iterates over the original float[] testing for an entry that matches k[0].
The moral of this story: you can easily shoot yourself in the foot by striving for an elegant solution in Java.
Often, dumb code is better. (Both faster, and more correct.)

Related

Convert a For loop to a lambda expression in Java

I have the following code
//assume we have a list of custom type "details" already constructed
for(int i = 0; i < details.size(); ++i) {
CallerID number = details.get(i).getNextNumber();
ClientData.addToClient(number);
}
I have oversimplified the code. The enum CallerID and the ClientData object work as intended. I am asking for help converting this loop to a lambda function so I can understand the logic of how to do so, then fill in the appropriate code as needed.
Let's first write it as a modern basic for loop and golf it a bit, just so we're comparing apples to apples:
for (var detail : details) clientData.addToClient(detail.getNextNumber());
And this is probably the right answer. It is local var, exception, and control flow transparent (which is what you want), and short.
The lambda form is this, but it's got downsides (mostly, those transparencies). It also isn't any shorter. You shouldn't write it this way.
details.stream().forEach(d -> clientData.addToClient(detail.getNextNumber());
You may be able to just remove stream() from that. But probably not.
Generally when people say "I want it in lambda form", that's not because someone is holding a gun to your head - you are saying that because somebody peddling a religion of sorts to you told you that 'it was better' and that this 'will scale'. Realize that they are full of it. There can be advantages to 'functional style', but none of these snippets are functional. A true functional style would involve a bunch of side-effect-free transformations, and then returning something.
.addToClient? You've lost the functional game there - you would want to instead convert each detail to something (presumably a ClientID), and from there construct an immutable object from that stream. You'd 'collect' your ClientIDs into a clientData object.
Let's say for example that clientData is just a 'list of ClientIDs' and nothing more. Then you'd write something like this:
var clientData = details.stream()
.map(MyDetailClass::getNextNumber)
.collect(Collectors.toList());
Is this better? No. However, if you're looking for 'a stream-style, lambda-based functional take on things', that qualifies. The output is constructed by way of collection (and not forEach that does a side-effect operation), and all elements involved are (or can be) immutable.
There's no particular reason why you'd want this, but if for some reason you're convinced this is better, now you know what you want to do. "Just replace it with a lambda" doesn't make it 'functional'.
I am asking for help converting this loop to a lambda function so I can understand the logic of how to do so, then fill in the appropriate code as needed.
A Function returns a value. As you are just updating something what you need is a Consumer which accepts a single argument of a list of some detail. Assuming those are in a Class named SomeDetails, here is how you would do it.
As you iterating over some structure limited by size and using get(i) I am presuming a list is required here.
List<SomeDetails> details = new ArrayList<>(); // then populated
// lambda definition
Consumer<List<SomeDetails>> update = (lst)-> {
for(SomeDetails detail : lst) {
CallerID number = detail.getNextNumber();
ClientData.addToClient(number);
}
};
And then invoke it like this, passing the List.
update.accept(details);
All the above does is encapsulate the for loop (using the enhanced version for simplicity) and perform the operation.
If this is all you wanted, I would recommend just doing it as you were doing it sans the lambda.

Java - Empty int array

I need to pass an empty int array.
new int[]{} -> this works.
But, is there anyway with the below approach?
Collections.emptyList().toArray() -> I am unable to cast this to int[] array.
The method signature,
public void checkVersions(final int[] versions)
This is the method to be called. There are case where i need to pass some empty int[].
Thanks
This might be considered off-topic to the question, but I still want to provide you with the following thougts:
When you write code, you should write it in a way that makes it as simple as possible to read later on by somebody who has no clue what the code is supposed to do.
Therefore, "new int[0]" or "new int[]{}" are much better than "IntStream.empty().toArray()". Why? Because the first two make it clear that you are constructing an int[] and that it is empty. The later solution with the IntStream requires more thought (thus has higher cognitive load) as you first see the IntStream, of which and empty stream is created. This empty stream of integers is then converted to an array. So you don't see the data type that is being created and you have an extra step (the conversion).
I would rate (personal thought!) other solutions than "new int[0]" or "new int[]{}" to be tricky code. Don't try to be fancy with a plain "empty integer array" creation, it will just cause pain to anybody who reads the code.
Now, I don't want to talk bad about you interest in alternatives, I only want to avoid that you put such code into production. I hope this message came along.
try this one
int[] array = IntStream.empty().toArray();

Optimizing Java Array Copy

So for my research group I am attempting to convert some old C++ code to Java and am running into an issue where in the C++ code it does the following:
method(array+i, other parameters)
Now I know that Java does not support pointer arithmetic, so I got around this by copying the subarray from array+i to the end of array into a new array, but this causes the code to run horribly slow (I.e. 100x slower than the C++ version). Is there a way to get around this? I saw someone mention a built-in method on here, but is that any faster?
Not only does your code become slower, it also changes the semantic of what is happening: when you make a call in C++, no array copying is done, so any change the method may apply to the array is happening in the original, not in the throw-away copy.
To achieve the same effect in Java change the signature of your function as follows:
void method(array, offset, other parameters)
Now the caller has to pass the position in the array that the method should consider the "virtual zero" of the array. In other words, instead of writing something like
for (int i = 0 ; i != N ; i++)
...
you would have to write
for (int i = offset ; i != offset+N ; i++)
...
This would preserve the C++ semantic of passing an array to a member function.
The C++ function probably relied on processing from the beginning of the array. In Java it should be configured to run from an offset into the array so the array doesn't need to be copied. Copying the array, even with System.arraycopy, would take a significant amount of time.
It could be defined as a Java method with something like this:
void method(<somearraytype> array, int offset, other parameters)
Then the method would start at the offset into the array, and it would be called something like this:
method(array, i, other parameters);
If you wish to pass a sub-array to a method, an alternative to copying the sub-array into a new array would be to pass the entire array with an additional offset parameter that indicates the first relevant index of the array. This would require changes in the implementation of method, but if performance is an issue, that's probably the most efficient way.
The right way to handle this is to refactor the method, to take signature
method(int[] array, int i, other parameters)
so that you pass the whole array (by reference), and then tell the method where to start its processing from. Then you don't need to do any copying.

Java: Setting an array of predetermined length as a method parameter?

I have a method that takes 5 double values and performs an action with them. Right now the argument list is five different doubles. Is there any way to pass a double[] as an argument to the method but make sure its length is exactly 5?
One way is this:
private void myMethod(double[] args) {
if (args.length == 5) {
// do something
}
}
but is there a better way?
If you know you need exactly 5 doubles, then I think you are better off asking for 5 distinct doubles. Having them listed out with meaningful names it will still be hard enough (even with intellisense or whatever it's called) to keep the order of the variables straight. If they are in an array, the user will need to consult the documentation to see which value should go in which index.
No. You can't restrict the length of an array passed to a function.
If your goal is to keep the checking code out of the method so it's cleaner, you could delegate the real work to another method.
If your concern is the length of the parameter list you could pass a parameter object.
You could create a class which is a specialization of a Vector limited to 5 doubles, but it seems like overkill. I would just throw an exception if there are too few or too many entries in the array - this is likely a programming problem rather than a runtime exception.
You could put your code in try-catch block. This provides to miss an unnecessary check.
But if something doing wrong you could avoid the problems with exception.

Difference between byte[] and byte ... in Java Methods

Someone asked me what the difference between the two method parameters and why you would use the ... over specifically assigned array.
putMessage(byte ...send)
putMessage(byte[] send)
I couldn't answer them with confidence and couldn't remember what the ... is called.
The ... in your first example are called varargs. Your second example has an array argument. Varargs are a convenience for times when you want to hard code a variable number of arguments to a method but don't want to manually create an array to hold them. It's a shorthand notation. Consider this:
putMessage(0b00100101, 0b00100101, 0b00100101); // varargs
vs. this:
putMessage(new byte[] { 0b00100101, 0b00100101, 0b00100101 }); // array
The first example is less cluttered and more readable.
The parameters with ellipses are generally referred to as "varargs" if you want to google that.
Using varargs allows you to call a method with variable number of arguments without having to specify an array e.g.
public void printStr(String ...strings) {
for (String s : strings) {
System.out.println(s);
}
}
> printStr("Hello", "World")
Hello
World
So varargs allow a certain degree of convenience, but there are downsides - the varargs parameter must be the last parameter in the method signature, and thus you cannot have more than one varargs parameter to a method. If you want to pass multiple arrays to a method you have to use arrays, not varargs.
Another reason you might see arrays in some places where you might expect varargs is that varargs were only introduced in Java 5 - older code and code that needs to be backwards compatible will still be using arrays even where it might make more sense conceptually to use varargs.
The advantage of using varargs in the method signature is flexibility - there are some situations where the caller will have an array ready anyway and some where they will just have several arguments. Varargs will accept either the array or each variable as a separate argument, saving the caller the trouble of instantiating and populating an array.
The first one is with Varargs.
In short
A. First can be used to call with single byte type arg, 2 byte args.. or many args or an array.
B. second will be used with array only.
The ellipsis (three dots) indicates that you are using "varargs".
See http://download.oracle.com/javase/1,5.0/docs/guide/language/varargs.html for more details.
Inside the method, you access the elements of "send" as an array. The two methods are the same in that regard. The convenience is for the caller. In the second putMessage, the caller is compelled to create an array of bytes to pass to putMessage. In the first putMessage, the caller can simply say "putMessage(byte1, byte2)" or "putMessage(byte1, byte2, byte3)" or "putMessage(byte1)" -- variable number of arguments, or varargs.
The ellipses (...) allow you to inline N parameters of a type to a function call without having to define an array first. In the end you do simply get an array of parameters but it's basically shorthand or syntactic sugar. Also your client code might be a little cleaner and more declarative with the ellipses syntax... though it could easily go the other way and become mucky and unreadable.
Here's a great example of the ellipses syntax (variable length argument lists.) While looking at the sample consider what the client code (in the main function) would look like if an array was used instead of a variable length argument list.

Categories

Resources