I'm brushing up on my data structure skills. I found a great free book online called Open Data Structures in Java. After reading through it, I'm trying to create all the stated data structures with the code provided so I can instill them in to my memory.
I ran in to an "error" and for the life of me I can't figure it out: in the resize() method for the ArrayStack (section 2.1.2), there is the line of code - T[] b = newArray(Math.max(n*2,1));. The point of this is so the array, which contains the elements, is neither too small or too large. If I use this line of code I get the following error message from Eclipse:
The method newArray(int) is undefined for the type ArrayStack<T>.
So, I'm thinking that it must have been a "typo" and what was meant was "new Array". But fixing that leaves me with the following error message from Eclipse:
Type mismatch: cannot convert from Array to T[].
I don't understand what I'm missing or doing wrong. So to sum up my question, how do you declare and instantiate a new generic array, particularly at a fixed size?
Given the class of T, let's call it klass...
For a one-dimensional array of length n:
T[] arr = (T[]) Array.newInstance(klass, n)
For a two-dimensional array of length n x m:
T[][] 2dArr = (T[][]) Array.newInstance(klass, n, m)
The above are actually two different functions, one takes an int argument and the second takes an int... argument, which you can also pass as an array. Both return an Object for which you need an unchecked cast.
If you want a jagged array of length n, second dimension undetermined, you will have to get the class of T[], let's call it klass2, and then do
T[][] 2dArr2 = (T[][]) Array.newInstance(klass2, n)
This is why you also need to pass in a type to collection.toArray(T[] arr), otherwise you get an Object[] for the vanilla toArray() method because it doesn't know the type.
What you would like is:
void resize() {
T[] b = new T[Math.max(n*2,1)];
for (int i = 0; i < n; i++) {
b[i] = a[i];
}
a = b;
}
But that does not work because T is not actually known at runtime, and it would have to be. However this can be written with a generic-safe constructor.
void resize() {
T[] b = (T[]) Array.newInstance( a.getClass().getComponentType(),
Math.max(n*2,1) );
for (int i = 0; i < n; i++) {
b[i] = a[i];
}
a = b;
}
It appears that the author meant to have a method, newArray in that class:
void T[] newArray(int size) {
return (T[]) Array.newInstance( a.getClass().getComponentType(), size);
}
Java does not make this a simple matter. Because of type erasure the class of T is not available at runtime (which is when you need to determine what type of array to create).
However, since you already have an array (a), you can use reflection to create a new array of that type.
It will look something like this:
import java.lang.reflect.Array;
public class Test {
public static void main(String args[]) throws Exception {
Object array[] = new Object[5];
array = resizeArray(array, 10);
for (Object o : array) {
System.out.println(o);
}
}
public static <T>
T[] resizeArray(T[] a, int newSize) throws Exception {
T[] b = (T[]) Array.newInstance(a.getClass().getComponentType(),
newSize);
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
return b;
}
}
Related
I've got a generic array class and I want to return an array in the main so I can use the sort method that I have ready in the main. I understand that the constructor has an array in it so I'm wondering if I can use that. Or do I need to set up a new method to return this.array ? Also it returns a generic array, how do I choose the type in main?
public class dynamicArray <T>{
private int index;
private T[] array;
public dynamicArray() {
array = (T[])new Object[10];
this.index = 0;
}
public T [] populate() {
return this.array;
}
Here I chose the integer type for the class. I'm not sure how can I extract the
array from the constructor.
public static void main(String[] args) {
dynamicArray<Integer>array = new<Integer>dynamicArray();
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
int [] arr = array.populate();
mergeSort(arr);
System.out.println(array.toString());
}
Unfortunately, arrays and generics don't work well together. Take a look at the source code of java's ArrayList - it is implemented with an Object[] and not a T[] - then every method will cast to T (which costs literally zero, it's just ugly and causes compiler warnings). I advise you do the same here: Arrays actually KNOW their component type (unlike a list of Ts, which does not, there is no method on a java.util.List that you can invoke to get the component type), and therefore casting Object[] to T[] is just wrong; java allows this solely for backwards compatibility reasons.
Basically, you can't work with T[] without things being subtly wrong and a lot of compiler errors.
In this specific case? I would strenuously advise you to use a private List<T> array; field instead of a T[] field.
Your call to array.populate() (that seems like a bizarre name for this method!) IS retrieving the array you created in the constructor. You are doing what you're asking for: "Extracting the array from the constructor" - invoking populate() on the object returned by the new dynamicArray<Integer>() is doing exactly that.
NB: You have a typo in your source code. it's new dynamicArray<Integer>();, not new<Integer>dynamicArray();. Perhaps that's causing some issues?
NB2: Java conventions dictate it's DynamicArray, and something like getBackingArray (instead of populate).
I think you ask two question :
How to set Integer type of that array object.
How to get Integer[] to int[]
Here is the code :
private int index;
private T[] array;
public dynamicArray() {
array = (T[])new Object[10];
this.index = 0;
}
public T [] populate() {
return this.array;
}
public void add(T x) {
array[++index] = x;
}
public static void main(String[] args) {
dynamicArray<Integer>array = new<Integer>dynamicArray();
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
int[] arr = Arrays.stream(array.populate())
.mapToInt(i -> i)
.toArray();
System.out.println(array.toString());
}
Answer for 1st question is you can not set Integer type because there wasn't any add method in your class. Answer for 2nd question is you try to convert Integer[] to int[] but there is no direct way to cast this. you just need to change Integer -> Object then Object -> int. This can be done easily using streams which is in Java 8 and i have used lambda here for showing power of lambda function.
Here is a possible alternative. Pass the type of array to the constructor. But essentially you are creating a limited form of ArrayList so you may just as well use that. Note that this still has the limitation that you can't use primitive arrays as the array type.
dynamicArray<Integer> array = new dynamicArray<>(new Integer[0]);
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
Integer[] a = array.getArray();
System.out.println(Arrays.toString(a));
}
class dynamicArray<T> {
private int size = 0;
private T[] array;
public dynamicArray(T[] a) {
array = a;
}
public void add(T value) {
if (array.length == size) {
array = Arrays.copyOf(array, size == 0 ? 10 : size*2);
}
array[size++] = value;
}
#SuppressWarnings("unchecked")
public T[] getArray() {
// need to copy the array since the length and size could be different.
T[] arrayCopy = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
System.arraycopy(array, 0, arrayCopy, 0, size);
return arrayCopy;
}
}
I am doing some tests with generic-methods and I'd like to transform these two methods below (convertFloatListToArray and convertShortListToArray) in just one (convertListToArray):
public class Helper{
public static float[] convertFloatListToArray(List<Float> list){
float[] array = new float[list.size()];
for(int i = 0; i<list.size(); i++){
array[i] = list.get(i);
}
return array;
}
public static short[] convertShortListToArray(List<Short> list){
short[] array = new short[list.size()];
for(int i = 0; i<list.size(); i++){
array[i] = list.get(i);
}
return array;
}
}
But when I try to use generics, as below, I have some errors:
public class Helper{
public static <T, E> T convertListToArray(List<E> list){
T array = new T[list.size()];
for(int i = 0; i<list.size(); i++){
array[i] = list.get(i);
}
return array;
}
}
I can understand java limitations about generics, but I wonder if someone know any solution, using generic-method, that I am not seeing.
As of the current version (Java 12), primitive types can't be represented with Java generics. More specifically, we can't provide a primitive type as a type argument. (We can't do e.g. Foo<int>.) We also can't use type variables as the type in a new expression, so we can't do new T[n] to create an array. Therefore, there's no ideal way to do this.
It is possible to do this reasonably using some reflection (java.lang.reflect.Array), but we need to provide a Class as an argument. Here's an example of how it might be done:
/**
* Unboxes a List in to a primitive array.
*
* #param list the List to convert to a primitive array
* #param arrayType the primitive array type to convert to
* #param <P> the primitive array type to convert to
* #return an array of P with the elements of the specified List
* #throws NullPointerException
* if either of the arguments are null, or if any of the elements
* of the List are null
* #throws IllegalArgumentException
* if the specified Class does not represent an array type, if
* the component type of the specified Class is not a primitive
* type, or if the elements of the specified List can not be
* stored in an array of type P
*/
public static <P> P toPrimitiveArray(List<?> list, Class<P> arrayType) {
if (!arrayType.isArray()) {
throw new IllegalArgumentException(arrayType.toString());
}
Class<?> primitiveType = arrayType.getComponentType();
if (!primitiveType.isPrimitive()) {
throw new IllegalArgumentException(primitiveType.toString());
}
P array = arrayType.cast(Array.newInstance(primitiveType, list.size()));
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
}
Example call:
List<Integer> list = List.of(1, 2, 3);
int[] ints = toPrimitiveArray(list, int[].class);
Note that Array.set will perform a widening primitive conversion, so the following works:
List<Integer> list = List.of(1, 2, 3);
double[] doubles = toPrimitiveArray(list, double[].class);
But it won't perform a narrowing conversion, so the following throws an exception:
List<Integer> list = List.of(1, 2, 3);
byte[] bytes = toPrimitiveArray(list, byte[].class); // throws
If you wanted, that code could also be used to make duplication easier:
public static int[] toIntArray(List<Integer> list) {
return toPrimitiveArray(list, int[].class);
}
public static double[] toDoubleArray(List<Double> list) {
return toPrimitiveArray(list, double[].class);
}
...
(Having multiple methods like that isn't really generic, though.)
One solution that you'll sometimes see places looks something like this:
public static <P> P toPrimitiveArray(List<?> list) {
Object obj0 = list.get(0);
Class<?> type;
// "unbox" the Class of obj0
if (obj0 instanceof Integer)
type = int.class;
else if (obj0 instanceof Double)
type = double.class;
else if (...)
type = ...;
else
throw new IllegalArgumentException();
Object array = Array.newInstance(type, list.size());
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return (P) array;
}
There are a variety of problems with that, though:
We don't know what type of array to create if the list is empty.
Doesn't work if there's more than one type of object in the list.
Unchecked casting of the result array to P, so there's a danger of heap pollution.
It's much better to just pass in a Class as an argument.
Also, while it's possible to just write many overloads which unbox arrays:
public static int[] unbox(Integer[] arr) {...}
public static long[] unbox(Long[] arr) {...}
public static double[] unbox(Double[] arr) {...}
...
Because of the effects of type erasure, it's impossible to write overloads which unbox many different types of List, as in the following:
public static int[] unbox(List<Integer> list) {...}
public static long[] unbox(List<Long> list) {...}
public static double[] unbox(List<Double> list) {...}
...
That won't compile, because we aren't allowed to have more than one method in the same class with the same name and erasure. The methods would have to have different names.
As a side-note, here are some non-generic solutions:
As of Java 8 we can unbox Lists of Integer, Long and Double using the Stream API:
List<Long> list = List.of(1L, 2L, 3L);
long[] longs = list.stream().mapToLong(Long::longValue).toArray();
Google Guava has Collection unboxing methods in their com.google.common.primitives classes, for example Doubles.toArray:
List<Double> list = List.of(1.0, 2.0, 3.0);
double[] doubles = Doubles.toArray(list);
I've been coding with C++ in school for 3 years now. I've started coding in Java just 2 days ago; my question is:
Is it bad practice to make generic arrays? What would be the alternative?
I am stumped and I can't seem to make a generic array besides doing something weird such as this example:
//Class implementing the MergeSort algorithm with generic types
// Revised by Doina January 2014
package Sorting;
import java.lang.*;
public class MergeSort {
// Wrapper method for the real algorithm
// T is the generic type which will be instantiated at runtime
// elementas are required to be comparable
public static <T extends Comparable<T>> void sort(T[] a) {
mergesort(a, 0, a.length - 1);
}
// Recursive mergesort method, following the pseudocode
private static <T extends Comparable<T>> void mergesort(T[] a, int i, int j) {
if (j - i < 1) return;
int mid = (i + j) / 2;
mergesort(a, i, mid);
mergesort(a, mid + 1, j);
merge(a, i, mid, j);
}
// Merge method
// Here we need to allocate a new array, but Java does not allow allocating arrays of a generic type
// As a work-around we allocate an array of type Object[] the use type casting
// This would usually generate a warning, which is suppressed
#SuppressWarnings("unchecked")
private static <T extends Comparable<T>> void merge(T[] a, int p, int mid, int q) {
Object[] tmp = new Object[q - p + 1];
int i = p;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= q) {
if (a[i].compareTo(a[j]) <= 0)
tmp[k] = a[i++];
else
tmp[k] = a[j++];
k++;
}
if (i <= mid && j > q) {
while (i <= mid)
tmp[k++] = a[i++];
} else {
while (j <= q)
tmp[k++] = a[j++];
}
for (k = 0; k < tmp.length; k++) {
a[k + p] = (T) (tmp[k]); // this is the line that woudl generate the warning
}
}
// Main methos to test the code, using Integer Objects
public static void main(String[] args) {
Integer[] a = new Integer[5];
a[0] = new Integer(2);
a[1] = new Integer(1);
a[2] = new Integer(4);
a[3] = new Integer(3);
a[4] = new Integer(-1);
// T will be instantiated to Integer as a resutl of this call
MergeSort.sort(a);
// Print the result after the sorting
for (int i = 0; i < a.length; i++)
System.out.println(a[i].toString());
}
}
It's not that it's a bad idea per se; it's just that generics and arrays don't mix very well.
The reason is due to covariance and invariance. Arrays are covariant (Integer[] is an Object[] because Integer is an Object, but generic classes are invariant (List<Integer> is not a List<Object> even though an Integer is an Object).
You also have to deal with unchecked casts, which defeat the entire purpose of generics. The most common way to create a generic array - E[] foo = (E[]) new Object[10]; - is not type-safe and can't be enforced at compile time. It's possible to reason about it at runtime, but the compile-time checks which generics bring to the table are lost at that point.
To answer the question directly, where and when possible, you want to use Java Collections instead, as they play very nicely with generics.
Just glancing at your supplied code, I imagine that using List<T> instead of T[] would get you by most of your problems (and I would hope that you're passing an ArrayList in since those operations can become expensive with a linked list).
It's not bad practice to create a generic array, but doing so correctly is so cumbersome people usually avoid it.
The reason it is cumbersome is that generics are erased, while arrays are reified. That is, type parameters are erased during compilation, while the component type of arrays is retained. Therefore, the runtime knows the component type of every array, but has forgotten the type arguments of all objects, i.e. the line
E[] array = new E[10];
does not compile because the runtime needs to know the component type for the new array, but has forgotten was E is.
The workaround in Makoto's answer:
E[] array = (E[]) new Object[10];
is not a good idea, as it actually creates an Object[], but then pretends to the compiler that is an E[]. As the the runtime has forgotten was E is, this cast also succeeds at runtime, even though it is not type correct. However, the runtime still enforces memory safety, by performing an additional check as soon as it is able, i.e. when the object is stored in a variable whose type is not generic. For instance:
static <E> E[] createArray(int size) {
return (E[]) new Object[size];
}
public static void main(String[] args) {
String[] array = createArray(size); // throws ClassCastException
for (String s : array) {
// whatever
}
}
That is, this workaround is hack that only works under certain circumstances, and will cause highly puzzling behaviour otherwise (a ClassCastException in a line of code that does not contain a cast ...).
The only way to create an E[] is through reflection, by providing the class object of our desired component type:
Class<E> eClass = ...;
E[] array = Arrays.newInstance(eClass, 10);
but how can we get this class object? If our caller knows, they can pass us a class literal (like Integer.class), or we can use reflection on some other object. In your case, you have another E[] at hand, so you can ask that array what E is:
E[] originalArray = ...;
Class<E> eClass = (Class<E>) originalArray.getClass().getComponentType();
E[] newArray = (E[]) Array.newInstance(eClass, size);
This will ensure the new array is of the same type as the old one, which is E[], unless somebody lied to us about the type of that array using Makoto's workaround.
As you can see, it is possible to create a generic array, but it is so cumbersome that people usually go the great lengths to avoid it. The usual alternative are using an array of some super type (in your merge sort, Comparable[] might work even better than Object[], because you would not have to cast), or using an ArrayList instead.
Adding to Makoto's answer,I would say that Arrays are covariant due to the fact that their type information is available at runtime whereas generic classes are invariant as type information is not available due to type erasure at compile time.
Covariant Arrays :-
Object[] covariantArrays = new String[5];
covariantArrays[0] = new Dog(); // will give java.lang.ArrayStoreException
Invariant Arrays :-
List invariantArrays = new List<String>();
invariantArrays.add(new Dog()); // Works fine as type information is not available
For this reason Generic arrays don't go well as Generics are limited to compile type safety and Arrays have real type information available even at Runtime
My goal here is to implement a method that will concatenate an arbitrary number of arrays into a single array of their common supertype, returning the resulting (typed) array. I have two implementations.
The first (this one doesn't need to be simplified):
public static <T> T[] concatArrays(Class<T> type, T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arr.length;
}
T[] all = (T[]) Array.newInstance(type, totalLen);
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
Let's create some arrays:
Long[] l = { 1L, 2L, 3L };
Integer[] i = { 4, 5, 6 };
Double[] d = { 7., 8., 9. };
Our method is called with:
Number[] n = concatArrays(Number.class, l, i, d);
This works and is completely type-safe (e.g., concatArrays(Long.class, l, i, d) is a compiler error), but it's somewhat annoying to specify Number.class if it's not necessary. So I implemented the following method (this is the one I want to simplify):
public static <T> T[] arrayConcat(T[] arr0, T[]... rest) {
Class commonSuperclass = arr0.getClass().getComponentType();
int totalLen = arr0.length;
for (T[] arr: rest) {
totalLen += arr.length;
Class compClass = arr.getClass().getComponentType();
while (! commonSuperclass.isAssignableFrom(compClass)) {
if (compClass.isAssignableFrom(commonSuperclass)) {
commonSuperclass = compClass;
break;
}
commonSuperclass = commonSuperclass.getSuperclass();
compClass = compClass.getSuperclass();
}
}
T[] all = (T[]) Array.newInstance(commonSuperclass, totalLen);
int copied = arr0.length;
System.arraycopy(arr0, 0, all, 0, copied);
for (T[] arr: rest) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
This is nicer to use from the client's perspective:
Number[] n = arrayConcat(l, i, d);
And again, the compiler is smart enough to give an appropriate error on Long[] all = arrayConcat(l, i, d). Since the compiler is able to recognize this error, it is clear that I am performing work at runtime (determining the common superclass of the given arrays) that the compiler is able to perform at compile time. Is there any way to implement my method without using my reflection-based method for determining a common superclass for the array creation step?
I tried this approach:
public static <T> T[] arrayConcat(T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arrays.length;
}
Object[] all = new Object[totalLen];
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return (T[]) all;
}
but this throws a ClassCastException upon returning. Obviously new T[totalLen] is also out. Does anyone have any other ideas?
You can do something like this:
public static <T> T[] arrayConcat(T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arr.length;
}
T[] all = (T[])Array.newInstance(
arrays.getClass().getComponentType().getComponentType(), totalLen);
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
This takes advantage of the fact that when using varargs, the compiler constructs an array of the components, and the array's type is properly set up such that the component type is the vararg elements type. In this case the array has type T[][], so we can extract T and use it to construct our T[].
(One exception is if the caller calls it using a generic type as the varargs type, then the compiler can't construct the proper array type. However, if the caller does this it will generate a warning in the caller code (the infamous varargs generics warning), and so the caller is warned that bad things will happen, so it's not our fault.)
One amazing thing about this solution is that it does not produce the wrong answer even when the user passes zero arrays! (As long as the compiler compiles it successfully, it would have inferred (or been specified explicitly) some concrete type T such that T[] is a valid return type. That type T is given to us in the type of arrays) Apparently the compiler doesn't ever infer correctly in this case.
Note that a caller can manually pass the "arrays" argument, and in such case, it could have runtime type of U[][], where U is a subtype of T. In such a case, our method will return an array of runtime type U[], which is still a correct result, as U[] is a subtype of T[].
Simple answer: no. Although the type checker has done some work as far as inferring T, this information is erased by the time it gets to bytecode. Erasure is at the core of Java generics; understand it, and you'll understand generics.
Btw, generics and arrays don't mix well. If you try to concatenate a bunch of List<String>[]s into a single List<String>[], you're going to get compiler warnings (and general lack of type safety).
Your example #2 is actually doing more work at runtime than the compiler is able to do. Consider:
Number[] longs1 = new Long[] { 1L, 2L, 3L };
Number[] longs2 = new Long[] { 4L, 5L, 6L };
Number[] concatted = arrayConcat(longs1, longs2);
The compiler only knows that concatted is a Number[], but your method will (at runtime) figure out that the common type is actually Long[].
What is wrong with this conversion?
public int getTheNumber(int[] factors) {
ArrayList<Integer> f = new ArrayList(Arrays.asList(factors));
Collections.sort(f);
return f.get(0)*f.get(f.size()-1);
}
I made this after reading the solution found in Create ArrayList from array. The second line (sorting) in getTheNumber(...) causes the following exception:
Exception in thread "main" java.lang.ClassCastException: [I cannot be cast to java.lang.Comparable]
What is wrong here? I do realize that sorting could be done with Arrays.sort(), I'm just curious about this one.
Let's consider the following simplified example:
public class Example {
public static void main(String[] args) {
int[] factors = {1, 2, 3};
ArrayList<Integer> f = new ArrayList(Arrays.asList(factors));
System.out.println(f);
}
}
At the println line this prints something like "[[I#190d11]" which means that you have actually constructed an ArrayList that contains int arrays.
Your IDE and compiler should warn about unchecked assignments in that code. You should always use new ArrayList<Integer>() or new ArrayList<>() instead of new ArrayList(). If you had used it, there would have been a compile error because of trying to pass List<int[]> to the constructor.
There is no autoboxing from int[] to Integer[], and anyways autoboxing is only syntactic sugar in the compiler, so in this case you need to do the array copy manually:
public static int getTheNumber(int[] factors) {
List<Integer> f = new ArrayList<Integer>();
for (int factor : factors) {
f.add(factor); // after autoboxing the same as: f.add(Integer.valueOf(factor));
}
Collections.sort(f);
return f.get(0) * f.get(f.size() - 1);
}
You are trying to cast int[] to Integer[], this is not possible.
You can use commons-lang's ArrayUtils to convert the ints to Integers before getting the List from the array:
public int getTheNumber(int[] factors) {
Integer[] integers = ArrayUtils.toObject(factors);
ArrayList<Integer> f = new ArrayList<Integer>(Arrays.asList(integers));
Collections.sort(f);
return f.get(0)*f.get(f.size()-1);
}
there are two cause of this exception:
1
Arrays.asList(factors) returns a List<int[]> where factors is an int array
2
you forgot to add the type parameter to:
ArrayList<Integer> f = new ArrayList(Arrays.asList(factors));
with:
ArrayList<Integer> f = new ArrayList<Integer>(Arrays.asList(factors));
resulting in a compile-time error:
found : java.util.List<int[]>
required: java.util.List<java.lang.Integer>
Use java.utils.Arrays:
public int getTheNumber(int[] factors) {
int[] f = (int[])factors.clone();
Arrays.sort(f);
return f[0]*f[(f.length-1];
}
Or if you want to be efficient avoid all the object allocation just actually do the work:
public static int getTheNumber(int[] array) {
if (array.length == 0)
throw new IllegalArgumentException();
int min = array[0];
int max = array[0];
for (int i = 1; i< array.length;++i) {
int v = array[i];
if (v < min) {
min = v;
} else if (v > max) {
max = v;
}
}
return min * max;
}
I think you have found an example where auto-boxing doesn't really work. Because Arrays.asList(T... a) has a varargs parameter the compiler apparently considers the int[] and returns a List<int[]> with a single element in it.
You should change the method into this:
public int getTheNumber(Integer[] factors) {
ArrayList<Integer> f = new ArrayList<Integer>(Arrays.asList(factors));
Collections.sort(f);
return f.get(0) * f.get(f.size() - 1);
}
and possibly add this for compatibility
public int getTheNumber(int[] factors) {
Integer[] factorsInteger = new Integer[factors.length];
for(int ii=0; ii<factors.length; ++ii) {
factorsInteger[ii] = factors[ii];
}
return getTheNumber(factorsInteger);
}
Arrays.asList(factors) returns a List<int[]>, not a List<Integer>. Since you're doing new ArrayList instead of new ArrayList<Integer> you don't get a compile error for that, but create an ArrayList<Object> which contains an int[] and you then implicitly cast that arraylist to ArrayList<Integer>. Of course the first time you try to use one of those "Integers" you get an exception.
This works from Java 5 to 7:
public int getTheNumber(Integer... factors) {
ArrayList<Integer> f = new ArrayList<Integer>(Arrays.asList(factors));
Collections.sort(f);
return f.get(0)*f.get(f.size()-1);
}
In Java 4 there is no vararg... :-)
this is from Java API
"sort
public static void sort(List list)
Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface. Furthermore, all elements in the list must be mutually comparable (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the list)."
it has to do with implementing the Comparable interface
As far as I understand it, the sort function in the collection class can only be used to sort collections implementing the comparable interface.
You are supplying it a array of integers.
You should probably wrap this around one of the know Wrapper classes such as Integer.
Integer implements comparable.
Its been a long time since I have worked on some serious Java, however reading some matter on the sort function will help.