Hello fellow Stackoverflowers,
I have the following problem:
I have a situation, where I get an array of primitive numbers as an input. For example int[] or short[] or even byte[]. Now, I need to iterate over the code and do certain stuff, for example, write the numbers into a list. The problem is, however, that every type of number needs a certain list. No problem, I thought, and tried to use generics:
Object dataSet = provider.getDataArray();
Number[] copy = new Number[Array.getLength(dataSet)];
for(int i= 0; i < Array.getLength(dataSet); i++) {
copy[i] = (T) Array.get(dataSet, i);
}
This works beautifully. However, the problem is with performance. I know that is cannot be circumvented because Reflection and the occuring boxing of the primitives is costly. I am now searching for a pattern to reduce the amount of code, because writing
Object dataSet = provider.getDataArray();
Class<? extends Number> dataType = provider.getDataType();
Number[] copy = new Number[dataSet.length];
if(dataType == Float.class)
float[] dataSetAsFloat = (float[]) dataSet;
for(int i= 0; i < dataSet.length; i++)
copySet[i] = dataSetAsFloat[i];
else if (dataType == Double.class)
double[] dataSetAsDouble = (double[]) dataSet;
for(int i= 0; i < dataSet.length; i++)
copySet[i] = dataSetAsFloat[i];
....
is a very bloated solution, because the application in the program I'm writing is not as simple as shown here. Basically, I create several hundred lines of extra code because of this performance problem. Is there a solution to this? Perhaps a pattern I'm not aware of, or some really simple trick I'm not seeing?
I would be immensely grateful for a response.
Thanks.
Have you considered a strategy pattern that chooses a conversion strategy based on the data type? While it won't reduce much of the overall total code, it will help to modularize it.
public interface ArrayConversionStrategy<T extends Number> {
T[] convertArray
}
public class FloatConversionStrategy implements ArrayConversionStrategy<Float>
float[] convertArray(Object[] dataset) {
float[] dataSetAsFloat = new float[dataset.length];
for(int i= 0; i < dataSet.length; i++)
dataSetAsFloat [i] = dataset[i];
}
}
public class DoubleConversionStrategy { ... }
public class LongConversionStrategy { ... }
Then in the calling class have a map of data types to strategies
Map<Class<? extends Number>, ArrayConversionStrategy> map;
Object[] dataSet = provider.getDataArray();
Class<? extends Number> dataType = provider.getDataType();
ArrayConversionStrategy strategy = map.get(dataType)
return strategy.convertArray(dataSet);
Some of my generic syntax may be off here and may have some boxing/autounboxing that may need to be done, but just as a general strategy this may be useful.
Instead of unpacking the wrapper, you can use a getLong(int)/putLong for integers and getDouble(int)/putDouble for floating point. This will give you two methods which support all primitives types.
interface Array {
public long getLong(int idx);
public double getDouble(int idx);
public void setLong(int idx, long l);
public void setDouble(int idx, double d);
}
class ByteProvider implements Array {
}
class IntProvider implement Array {
etc.
Related
There is a program which handles many lists of many types (Person, Cat, Chairs etc..).
The following code appears many times in the code. The code fills a given list with instances of type Cat until the list has a given number of instances.
int howMany = max - cats.size();
for(int i = 0; i < howMany; ++i) {
cats.add(new Cat());
}
Since this code appears many time we want to create a method to replace it. The method will accept the List that we want to add instances to and the max number of instances. It can accept more parameters if needed.
This seems simple at first but then I remembered it's not possible to write new T() because of type erasure.
What other way is there to achieve this?
You can re-use a method, that instantiates a class of a given type T.
public T getInstanceOfT(Class<T> aClass) {
return aClass.newInstance();
}
Then, you can implement a method, that populates a given List<T> with n objects of type T.
public <T> void populateList(List<T> list, Class<T> clazz, int n) {
for (int i = 0; i < n; i++) {
list.add(getInstanceOfT(clazz));
}
}
Note1: You have to handle IllegalAccessException and InstatiationException.
Note2: If you don't want to use getInstanceOfT() you can just do clazz.newInstance() when adding to the list.
You can use the method as follows:
int howMany = max - cats.size();
Class<Cat> catClass = Cat.class;
List<Cat> listOfCats = new ArrayList<Cat>();
populateList(listOfCats, Cats.class, howMany);
More info:
Create instance of Generic type in Java
Instead of adding all those unnecessary objects to the list, couldn't you wrap the shorter lists in an object that makes them look longer?
class PaddedList<T> extends AbstractList<T> implements List<T> {
private final List<T> wrappedList;
private final int size;
private final T padding;
public PaddedList(List<T> wrap, int size, T pad) {
this.wrappedList = wrap;
this.size = size;
this.padding = pad;
}
#Override
public T get(int index) {
return index < wrappedList.size() ? wrappedList.get(index) : padding;
}
#Override
public int size() {
return size;
}
}
public void test() {
List<String> test = Arrays.asList("One", "Two");
List<String> list10 = new PaddedList<>(test, 10, "Many");
System.out.println(list10);
}
this prints
[One, Two, Many, Many, Many, Many, Many, Many, Many, Many]
Obviously this will go very wrong if you try to modify any of the objects that are used as padding but if all you want is your lists seeming like they are fixed length then this would work very efficiently.
I am currently implementing an algorithm to generate High Variance clusters in a dataset. Throughout my different tests, I will be using many different data types, so I need to make my Cluster class usable for any data type T. So, I have:
public class Cluster<T> {
private int score;
private ArrayList<T> list;
private int k;
and to be able to assess the effectiveness of this algorithm, we have a score that is computed based on the distance between each object in the cluster. Here is the computeScore() method:
private void computeScore() {
if (list.size() < 2) score = 0;
else if (list.size() == 2) {
T x = list.remove(1);
T y = list.remove(0);
score = x.distanceTo(y);
list.add(y);
list.add(x);
}
}
The problem is that in the computeScore() method, the line score = x.distanceTo(y) throws an error because for type T, the distanceTo() method is undefined. For all of my datatypes, I will be defining this method for use here. How do I tell the Cluster class, that distanceTo() will be defined?
Thanks in advance for your help.
I guess you could use <T extends YourClass> and define distanceTo() in YourClass.
This can be achieved with reflection;
something like... x.getClass().getMethod("distanceTo", (Class<?>[]) T).invoke(x);
So guys I have to write a generic method to find the maximum element in a 2-D array then I have to test using integers, strings, and objects.
I'm a little sleep deprived so I apologize for what is probably a very very simple fix.
I have my generic method:
public class Generic {
public static <T extends Comparable<T>> T Max(T[][]stuff) {
T max = stuff[0][0];
for (int i = 0; i < stuff.length; i++)
for(int j = 0; j <stuff.length; i++)
if (stuff[i][j].compareTo(max) > 0)
max = stuff[i][j];
return max;
}
}
and simply trying to test with integers first
public class GenericTester {
public static void main(String[] args) {
Integer[][] myArray = { {0,1,2,3}, {3,2,1,0}, {3,5,6,1}, {3,8,3,4} };
System.out.println(Generic.Max(myArray));
}
}
Ok I fixed the previous error, dumb mistake, but yes now I am getting The method Max(T[][]) in the type Generic is not applicable for the arguments (int[][])
what would be the best fix for this problem?
Thanks for any and all help
Presumably you need Generic.Max(myArray) or else you need to
import static Generic.Max;
at the top of GenericTester.java.
Generics will not work with primitive types, so T cannot be bound to int. Note, in particular, that int does not extend Comparable<int>. You will need to use an Integer[][] array instead of int and similarly for the other primitive types.
EDIT In addition to the above, your loops need some work. First, the increment on the inner loop is wrong (this is why you are seeing an ArrayIndexOutOfBoundsException). Second, your code requires that the matrix is square and full (since you use stuff.length for the inner loop limit). Here's how I would write them (using enhanced for loop syntax):
public class Generic {
public static <T extends Comparable<T>> T Max(T[][]stuff) {
T max = stuff[0][0];
for (T[] row : stuff) {
for (T elt : row) {
if (elt.compareTo(max) > 0) {
max = elt;
}
}
}
return max;
}
}
For a truly general method, you would want to check that stuff[0][0] exists.
So - I want to make a 2D array of generic lists containing some data I am interested in (on a grid of of some set size),
private ArrayList<MyDataStruct>[][] gridData;
When I initialize this array, I go,
gridData = (ArrayList<MyDataStruct>[][])new ArrayList[w][h];
for(int x=0; x<w; x++)
for(int y=0; y<h; y++){
gridData[x][y] = (ArrayList<MyDataStruct>)new ArrayList<MyDataStruct>(0);
//other stuff, i.e. get data from somewhere, etc
}
and I get a unchecked cast warning. Was curious about why this is not type safe and what I can do to make it type safe (short of switching out of using a 2d array). I understand that it's not very good to mix arrays and generics, but in this case I'm just writing this to run some experiments, so i'm using this instead of say lists of lists lists to save myself some time. Are there any other more elegant approaches that let's me get away with easy to read/write syntax (i.e. no nested lists), but are also type safe?
Thanks!
The only type-safe option is to use nested lists.
Because of type erasure, you can't do new ArrayList<MyDataStruct>[w][h], because that basically becomes new ArrayList[w][h]...and things get complicated and hairy, fast, because arrays don't behave like generics.
Java considers String[] to be a subclass of Object[], but ArrayList<String> isn't a subclass of ArrayList<Object>. Normally, arrays throw an ArrayStoreException when you try to put an element into an array that doesn't match the array's true type, but because of type erasure, it can't do that when generics are in the picture.
Josh Bloch gives the following example in Effective Java:
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1];
List<Integer> intList = Arrays.asList(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0);
The only way to prevent all this code from being entirely legal in Java is to ban the generic array creation, or at least mark it as unsafe.
That said, IMO, the moderate overhead of typing out List and doing it with generic lists is absolutely worth the type safety.
I got here with the same problem (just some experiments, "get things done" approach).
Here is a solution that seems to work for me, using reflection.
note that it is 100% agnostic to the type of the cells in the array (can be list, map, anything). perhaps with a little more work it can also be agnostic to the # of dimensions of the array, but I've bigger fish to fry today...
private static <T> T[][] makeArray(Class<T> componentType, int... dimentions) {
return (T[][]) Array.newInstance(componentType, dimentions);
}
attached below is a test method. to keep the type of the array generic I had to ugly-cast here. this can be handled in another wrapping method to keep client code nice and clean:
#Test
public void testMakeArray() throws Exception {
#SuppressWarnings("unchecked")
List<String>[][] arr = makeArray(List.class, 2, 3);
Assert.assertEquals(2, arr.length);
Assert.assertEquals(3, arr[0].length);
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = Arrays.asList("cell", "r="+i, "c="+j);
System.out.print(arr[i][j] + "\t");
}
System.out.println("");
}
}
it prints:
[cell, r=0, c=0] [cell, r=0, c=1] [cell, r=0, c=2]
[cell, r=1, c=0] [cell, r=1, c=1] [cell, r=1, c=2]
I was playing around with a similar idea, but instead used nested Lists of Optionals. It seems to provide some benefits as you do not need to check for null and can instead use isEmpty or isPresent for each cell. However, I'm sure for reasons unknown to me, this is probably not a great solution. I thought i'd share anyways.
public class Grid<T> {
private final int rowCount;
private final int columnCount;
private final List<List<Optional<T>>> data;
public Grid(int rowCount, int columnCount) {
this.rowCount = rowCount;
this.columnCount = columnCount;
data = build();
}
private List<List<Optional<T>>> build() {
return IntStream.range(0, rowCount)
.mapToObj(rowId -> IntStream.range(0, columnCount)
.mapToObj(columnId -> Optional.<T>empty())
.collect(Collectors.toList()))
.collect(Collectors.toList());
}
public Optional<T> get(int rowId, int columnId) {
return data.get(rowId).get(columnId);
}
public void set(int rowId, int columnId, T t) {
data.get(rowId).set(columnId, Optional.ofNullable(t));
}
}
I have to pass a primitive 2d array to a filtering routine.The algorithm for filtering(median filter) is same irrespective of the type of the array.Is there a way to pass any type of array in a generic manner or should I overload the same same function with different array types.In the second case the same code will have to be repeated for different data types.
int[][] medianfilter(int[][] arr){ ... }
float[][] medianfilter(float[][] arr){ ... }
Is there a way to make the above code a generic one,instead of repeating the code for medianfilter in each an every overloaded function ?
There is no good way to do this for primitive arrays, which is why all the library functions (such as java.util.Arrays) also have these duplicated methods.
You could define a method
Object[] medianfilter(Object[] arr); // note the missing dimension
and use reflection to find out the runtime type. This is what System.arraycopy is doing. But you then need to type-cast. Ugly.
int[][] result = (int[][]) medianFilter( input );
Go with the duplicated methods.
There is a way to pass the type of an array in a generic manner:
public T test(T[][] arg)
{
T[][] q = arg;
T[] r = q[0];
T s = r[0];
return s;
}
... unfortunately it won't work for primitive types. You'll need to use Integer and Float as your parameterized types.
The only way to pass it in a generic manner and keep it as a primitive array is as an Object. Personally, I'd just overload it, and see it as a cost of using primitives.
To avoid duplication of code in the algorithm (if it is a lot of code) you could produce an abstract class called something like DoubleAlgorithm with abstract methods like double getElement(int i, int j) and handleResult(double result) and then write very small subclasses of this, one for each primitive type.
Let me explain with an example (suppose the algorithm was adding the numbers).
public int filter(int [][] values) {
IntAlgorithm algo = new IntAlgorithm(values);
algo.run();
return algo.getResult();
}
public double filter(double [][] values) {
DoubleAlgorithm algo = new DoubleAlgorithm(values);
algo.run();
return algo.getResult();
}
public class AbstractAlgorithm {
public run() {
double sum = 0.0;
for(int i=0; i<rows(); i++) {
for(int j=0; j<columns(i); j++) {
sum+=getElement(i, j);
}
}
handleResult(sum);
}
protected abstract int rows();
protected abstract int columns(int row);
protected abstract double getElement(int i, int j);
protected abstract void handleResult();
}
public class IntAlgorithm extends AbstractAlgorithm {
int [][] values;
int result;
IntAlgorithm(int [][] values) {
this.values= values;
}
public int rows() {
return values.length;
}
public int columns(int row) {
return values[row].length;
}
public double getElement(int i, int j) {
return values[i][j];
}
public void handleResult(double result) {
this.result = (int)result;
}
public int getResult() {
return result;
}
}
As you can see, it is quite verbose, but if your algorithm was big it might be worth it. Hopefully it is obvious how to extend to your algorithm.
As Thilo has pointed out, it isn't safe to do all algorithms with just treating ints/longs as doubles, but for a number it will be good enough. If it isn't for you, then you need to go even more verbose, work out which properties of numbers you need (eg add) and extract those to a separate interface. For a median filter, I would expect just using doubles will work fine, but I'd test the edge cases.