Java generic methods difference between two arrays - java

I am trying to write a method that will find the difference between elements of two arrays of the same length and here is what I did until now:
public static <T extends Object> T method(T[] array,T[] array1){
T[]difference;
difference = new T[array.length]; //error :generic array creation
for(int i=0;i<array.length;i++){
difference[i]=array[i]-array1[i]; //error:bad operand types for binary operator
}
return (T)difference;
}
but it constantly returns to me a message that i wrote next to the line of the code that it refers to

There are two problems with your code:
Java doesn't support generic array creation. You can get round this by creating an array of Objects and then casting.
The bigger problem is that java doesn't support operator overloading, and subtraction (i.e. -) is only supported by primitive types. This is compouded by the fact that generics doesn't support primitive types, which makes what you're trying to do impossible on its own.
To get round these problems, you'd need to do a number of things:
Don't use primitives, but you can used boxed types instead (so instead of int you'd use Integer, etc.).
You need to tell your function how to find the difference between two objects of the required type. You can achieve this by defining a 'Subtractor' interface.
I'd also suggest using lists instead of arrays, as these handle generics much more nicely. You can easily convert between lists and arrays afterwards if needed.
So, as a skeleton of a solution, here's what I'd propose:
Define a generic interface for finding differences:
interface Subtractor<T> {
T subtract(T a, T b);
}
Rewrite your method to use lists, and to take a subtractor as an argument:
public static <T> List<T> difference(
List<? extends T> listA, List<? extends T> listB, Subtractor<T> subtractor) {
int resultSize = Math.min(listA.size(), listB.size());
List<T> result = new ArrayList<>(resultSize);
for(int i=0; i<resultSize; ++i) {
result.add(subtractor.subtract(listA.get(i), listB.get(i)));
}
return result;
}
Define subtractor implementations for the types you want to be able to use the method with:
class IntSubtractor implements Subtractor<Integer> {
#Override
Integer subtract(Integer a, Integer b) {
return Integer.valueOf(a.intValue() - b.intValue());
}
}
Use your method. You can use Arrays.asList for turning arrays into lists, but you can't use arrays of primitives unfortunately.
Integer[] a = new Integer[]{5,6,7,8};
Integer[] b = new Integer[]{1,2,3,4};
Integer[] c = difference(Arrays.asList(a), Arrays.asList(b), new IntSubtractor())
.toArray(new Integer[4]);

I assume that your array values are numbers. It is difficult to provide a general solution. But I would say this might work for your case:
public static <T extends Number> Number[] method(T[] array, T[] array1){
Number[]difference = new Number[array.length];
for(int i=0; i< array.length; i++){
difference[i]= Double.valueOf(array[i].doubleValue() - array1[i].doubleValue());
}
return difference;
}

Related

Why do the new Java 8 streams return an Object Array on toArray calls?

Whilst I was working on a project involving Java 8's new streams, I noticed that when I called Stream#toArray() on a stream, it return an Object[] instead of a T[]. Surprised as I was, I started digging into the source code of Java 8 and couldn't find any reason why they didn't implement Object[] toArray(); as T[] toArray();. Is there any reasoning behind this, or is it just an (in)consistency?
EDIT 1:
I noticed in the answers that a lot of people said this would not be possible, but this code snippet compiles and return the expected result?
import java.util.Arrays;
public class Test<R> {
private Object[] items;
public Test(R[] items) {
this.items = items;
}
public R[] toArray() {
return (R[]) items;
}
public static void main(String[] args) {
Test<Integer> integerTest = new Test<>(new Integer[]{
1, 2, 3, 4
});
System.out.println(Arrays.toString(integerTest.toArray()));
}
}
Try:
String[] = IntStream.range(0, 10).mapToObj(Object::toString).toArray(String[]::new);
The no-arg toArray() method will just return an Object[], but if you pass an array factory (which can be conveniently represented as an array constructor reference), you can get whatever (compatible) type you like.
This is the same problem that List#toArray() has. Type erasure prevents us from knowing the type of array we should return. Consider the following
class Custom<T> {
private T t;
public Custom (T t) {this.t = t;}
public T[] toArray() {return (T[]) new Object[] {t};} // if not Object[], what type?
}
Custom<String> custom = new Custom("hey");
String[] arr = custom.toArray(); // fails
An Object[] is not a String[] and therefore cannot be assigned to one, regardless of the cast. The same idea applies to Stream and List. Use the overloaded toArray(..) method.
About the reason why toArray() returns Object[]: it is because of type erasure. Generic types lose their type parameters at runtime so Stream<Integer>, Stream<String> and Stream become the same types. Therefore there is no way to determine component type of array to create. Actually, one could analyze types of array's elements using reflection and then try to find their least upper bound, but this is too complicated and slow.
There is a way to get R[] array by using overloaded toArray(IntFunction<A[]> generator) method. This method gives the caller an opportunity to choose type of the array. See this SO question for code examples: How to Convert a Java 8 Stream to an Array?.

Generic Types & the toArrayMethod

I have a class MyStack<T> which defines the following
public T[] toArray(){
int s=size();
#SuppressWarnings("unchecked")
T[] result=(T[])new Object[s];
Node n=first;
for (int i=0; i<s; i++){
result[i]=n.data;
n=n.next;
}
return result;
}
Since this returns an array of type T, I would think that if I declared this instance: MyStack<String> s=new MyStack<>, that the following would be perfectly valid: String[] test=s.toArray(). I think this because since s is of type String, toArray should return an array of type String, since String has basically been substituted in for every T in this class (only for this particular instantiation, I know). The only way this runs without errors is if I do this: Object[] test=s.toArray().
Why is this?
In a word, type erasure. Taken from the Java website:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
What this means is that, when your code is compiled, MyStack<String> is compiled into MyStack<Object>. This is to make sure that generics do not incur an overhead by needing to create new classes. How does this apply to you? Well..
MyStack<String> s = new MyStack<>();
is converted into..
MyStack<Object> s = new MyStack<>();
Now, this means that when you call the toArray method, the only type that can be guarenteed is the Object type. The compiler can't be sure that everything it returns is of type String, so it won't let you treat it as a String, due to the strong typing in Java. So, what is the only variable type left?
Object[] array = s.toArray();
Extra Reading
Type Erasure in Java.
Well, hold on a minute. Suppose your hypothesis were correct that String were substituted for every T.
Would the following cast be valid?
String[] result = (String[])new Object[s];
No, it would not. We can be sure that a new Object[] is not a String[].
Now sometimes you will see something like (T[])new Object[n] but it only works because the cast actually becomes erased inside the generic class. (It is a deceptive idiom.)
When the class gets compiled, what actually happens is that references to T are replaced with its upper bound (probably Object unless you had something like <T extends ...>):
public Object[] toArray(){
int s=size();
Object[] result=new Object[s];
Node n=first;
for (int i=0; i<s; i++){
result[i]=n.data;
n=n.next;
}
return result;
}
And the cast is moved to the call site:
MyStack stack = new MyStack();
String[] arr = (String[])stack.toArray();
So in fact, while the cast is erased inside the class, the cast does happen once the value is returned to outside the class, where ClassCastException is thrown.
The inability to instantiate arrays (and objects in general) generically is why the Collections framework defines their toArray method to take the return array as an argument. A simple version of this for you would be like the following:
public T[] toArray(T[] inArray){
int s = size();
Node n = first;
for (int i = 0; i < s; i++){
inArray[i] = n.data;
n = n.next;
}
return inArray;
}
For some ideas on how to create an array generically, you may see 'How to create a generic array in Java?'; however you will need the caller to pass some argument to the method.

Is there a way to use Java generic types to write sorting algorithms?

I've been relearning Java after a long time, and I'm trying out writing some sorting algorithms. The (rather outdated) textbook I have uses the Comparable interface to sort objects. Since Comparables are now generic types, doing this gives me a lot of warnings about raw types when compiling. After some research, it looks like I can do something like, for example:
public class Sorting
{
public static <T extends Comparable<T>> void quickSort(T[] list, int start, int end)
{
/*...*/
while((list[left].compareTo(list[pivot]) < 0) && (left != right)) // for example
left++;
/*...*/
}
}
The problem with this is that the naive way of calling this method does not work:
public class SortingTest
{
public static void main(String[] args)
{
// Produces an error, cannot create arrays of generic types
Comparable<Integer>[] list = new Comparable<Integer>[100];
/* fill the array somehow */
Sorting.quickSort(list, 0, 99);
}
}
It is illegal to create an array of generic types in Java. The problem only gets worse if I try to implement a merge sort, since that requires creating arrays of Comparable types inside the merge sort method itself.
Is there any way to handle this situation elegantly?
Note that T extends Comparable<T>. It doesn't have to be Comparable<T>.
So you could, for example, create an array of Integers, because Integer implements Comparable<Integer>.
Integer[] list = new Integer[100];
/* fill the array somehow */
Sorting.quickSort(list, 0, 99);
You have to make an array of Object and then cast to an array of T. Note that this will create a compiler warning.

Sorting an array of comparable interface

I have written sorting methods for an array of comparables, insertion, merge, and selection, I've done this by altering code I had before from sorting an int array, and I just changed things from int to Comparable. However, When I was doing it for int arrays, I knew very well how to actually use the method, for example this is my my selection sort for ints:
public void selectionSort(int[] list){
for (int i=0;i<list.length;i++){
for (int si=i;si<list.length;si++){
if (list[si]<list[i]){
int temp=list[i];
list[i]=list[si];
list[si]=temp;
}
}
}
}
and this is the code which ends up using this method:
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int numItems,searchNum,location;
Sorts sort=new Sorts();
int[]test;
System.out.print("Enter the number of elements: ");
numItems=in.nextInt();
test=new int[numItems];
for (int i=0;i<test.length;i++){
test[i]=(int)(100*Math.random());
}
System.out.println("Unsorted: ");
displayArray(test);
sort.selectionSort(test);
System.out.println("Sorted: ");
displayArray(test);
and everything works fine, but for my comparable selection sort, I have this code:
public static void selectionSort(Comparable[] list){
for (int i=0;i<list.length;i++){
for (int si=i;si<list.length;si++){
if (list[si].compareTo(list[i])<0){
Comparable temp=list[i];
list[i]=list[si];
list[si]=temp;
}
}
}
}
but when I get to writing the code to test out this method, I just have no idea how to approach it, I don't know how I can make an array of Comparable interfaces, the concept is just so confusing for me and I can't find a way to make it work.
Integer, for instance, implements Comparable, so it's legal to write:
Comparable[] list = new Comparable[3];
list[0] = Integer.valueOf(3);
list[1] = Integer.valueOf(2);
list[2] = Integer.valueOf(3);
You can see all the implementors of Comparable in the standard JDK by looking at the JavaDoc.
The trouble is (and you should see some compiler warnings about this), you can't both specify the generic parameter for Comparable and make an array of the parameterized object, that is, it's not legal to write:
Comparable<Integer>[] list = new Comparable<Integer>[3];
Even if it were legal to write that, you'd run into a new issue, since you need the concrete type to use in the Comparable<T> test. Comparable<T> requires a comparison to an object of type T (the method is int compareTo(T o)). In essence, your code only works because it's unparameterized (T is implicitly Object, everything extends Object), but you're losing some compile-time safety checks along the way.
It might make more sense to parameterize your input by an array of generic parameterized Comparable objects rather than as an array of Comparable. Writing this using generics is a little bit tricky, the method prototype would look something like:
public static void <T extends Comparable<T>> selectionSort(T[] list) {
// legal to call list[k].compareTo, because `T` is guaranteed to be `Comparable`
}
Notably, this will work for any non-primitive object type T that implements Comparable<T>, such as an Integer[] or Double[], but not an int[].

generics in Java - instantiating T

I have the following code in Java:
public static<T> void doIt(Class<T> t)
{
T[] arr;
arr = (T[])Array.newInstance(t, 4);
}
I want to be able to use doIt using both primitive type such as double and using class objects such as String.
I could do it by using (code compiles):
doIt(double.class);
doIt(String.class);
However, I am worried that in the first case, the Java compiler will actually wrap the double primitive type using a Double class, which I don't want. I actually want it to instantiate a primitive array in this case (while instantiating an objects array with the String case). Does someone know what happens with doIt(double.class)? Is it instantiated as Double or double?
Thanks.
You couldn't actually make T = double here - Java generics simply don't work with primitive types. However, you can still instantiate your array:
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) {
createArray(double.class);
}
private static void createArray(Class<?> clazz) {
Object array = Array.newInstance(clazz, 4);
System.out.println(array.getClass() == double[].class); // true
}
}
It really depends on what you want to do with the array afterwards.
You can make an array of primitive double like this:
double[] arr = (double[]) Array.newInstance(double.class, 0);
But you can't make this work with generics, because generic parameters are always reference types, not primitive types.
Generics will work with objects so it should be a Double after boxing.
You can create a method that takes an array type instead of the element type and get around the problem that type parameters must be reference types since all array types are reference types.
<T> T doIt(Class<T> arrayType) {
assert arrayType.getElementType() != null;
return <T> Array.newInstance(arrayType.getElementType(), 4);
}

Categories