Java Generics method code duplication issue regarding primitive arrays - java

So I'm working on an implementation of a Grid class for a small, personal utilities library. After much work, I'm tidying up the Grid class and adding some functionality. One of the key pieces of functionality that I wish to use with my Grid class is to be able to take a single, 2D array of any given type as an argument to the constructor.
This worked fine until I realized that I can't compile any code that attempts to pass in any array of primitives to the constructor. Since autoboxing doesn't happen on primitive arrays, this presents a design problem to me in the form of code reuse. The method for initializing the Grid is the same regardless of the type of array passed in, even if they are primitives, but it appears that I need to write a separate constructor for all the different types of primitives. Which leaves me with 9 different constructors assuming I just use my base constructor (and I had planned to have constructors with different arguments for things such as grid-wrapping options).
Am I right in assuming that there is no way to avoid all this code duplication?

You can avoid (most of) the duplication by using Array class. But it's not pretty: your constructor parameter would have do be of type Object, and you'd have to just trust your caller to pass the actual array there, and not a Socket or a Properties.
For example, you could do your own boxing like this:
<T> public T[][] boxArray(Class<T> clazz, Object array) {
int height = Array.getLength(array);
int width = height == 0 ? 0 : Array.getLength(Array.get(array, 0));
T[][] result = (T[][]) Array.newInstance(clazz, height, width);
for(int i = 0; i < height; i ++) {
Object a = Array.get(array, i);
for(int j = 0; j < width; j++) {
result[i][j] = (T) Array.get(a, j);
}
}
return result;
}

Related

How to work with arrays with an unknown number of dimensions in Java? [duplicate]

I need to be able to have an n-dimensional field where n is based on an input to the constructor. But I'm not even sure if that's possible. Is it?
Quick solution: you could approximate it with a non-generic ArrayList of ArrayList of ... going as deep as you need to. However, this may get awkward to use pretty fast.
An alternative requiring more work could be to implement your own type using an underlying flat array representation where you calculate the indexing internally, and providing accessor methods with vararg parameters. I am not sure if it is fully workable, but may be worth a try...
Rough example (not tested, no overflow checking, error handling etc. but hopefully communicates the basic idea):
class NDimensionalArray {
private Object[] array; // internal representation of the N-dimensional array
private int[] dimensions; // dimensions of the array
private int[] multipliers; // used to calculate the index in the internal array
NDimensionalArray(int... dimensions) {
int arraySize = 1;
multipliers = new int[dimensions.length];
for (int idx = dimensions.length - 1; idx >= 0; idx--) {
multipliers[idx] = arraySize;
arraySize *= dimensions[idx];
}
array = new Object[arraySize];
this.dimensions = dimensions;
}
...
public Object get(int... indices) {
assert indices.length == dimensions.length;
int internalIndex = 0;
for (int idx = 0; idx < indices.length; idx++) {
internalIndex += indices[idx] * multipliers[idx];
}
return array[internalIndex];
}
...
}
Here's a nice article that explains how to use reflection to create arrays at run-time: Java Reflection: Arrays. That article explains how to create a one-dimensional array, but java.lang.reflect.Array also contains another newInstance method to create multi-dimensional arrays. For example:
int[] dimensions = { 10, 10, 10 }; // 3-dimensional array, 10 elements per dimension
Object myArray = Array.newInstance(String.class, dimensions); // 3D array of strings
Since the number of dimensions is not known until runtime, you can only handle the array as an Object and you must use the get and set methods of the Array class to manipulate the elements of the array.
Try this:
https://github.com/adamierymenko/hyperdrive

Java : To write a method that can be applied on any kind of array

Hi and thanks for noticing my problem. I want to write a method that can be used by different types of arrays. But my code always looks like this:
public int indexOf_1(int[] a,int b){
//Find the first matched result and return, otherwise report -1
int index = -1;
for(int j=0;j<a.length;j++){
if (a[j]==b)
{index=j;}
}
return index;
}
public int indexOfChar_1(char[] a,int b){
//Consider merged to the previous method?
int index = -1;
for(int j=0;j<a.length;j++){
if (a[j]==b)
{index=j;}
}
return index;
}
That seems to be redundant and I'm completely uncomfortable with such code duplication. Is there any way to write a searching method for all kinds of array to avoid repeating in this case? Thanks!
Unfortunately because the way arrays and the JVM work, this can't be reduced. Not even generics can help since int[] cannot be safely cast to Object[] without explicit conversion.
This looks like a common util function. If you're not comfortable with the code duplication, you can consider using one of the many libraries which provide this functionality. Guava and Commons-Lang are a few.
Guava puts them in the class relevant to the primitive type. Commons-Lang arranges them in the ArrayUtils class
e.g.
Bytes.indexOf(byteArray, (byte) 2);
Ints.indexOf(intArray, 22);
ArrayUtils.indexOf(intArray, 6);
Well you could use Object[] but you might not want to use ==, since it will compare identity of objects instead of values, instead you probably want to use .equals(). (Unless you know the value will always be a char or int) Perhaps this:
public int indexOf(Object[] a, int b) {
int index = -1;
for (int j = 0; j < a.length; j++) {
if (a[j].equals(b)) {
index = j;
}
}
return index;
}
public static <T> int index_Of(Object[] input,T value){
//Find the first matched result and return, otherwise report -1
for(int j=0;j<input.length;j++){
if(input[j].equals(value))
return j;
}
return -1;
}
You can generalize you method to deal with all kind of arrays. However, please pay more attention to the type. If you want to use Object referring to primitive type, when declaring a primitive type array, you need to use reference type. For example,
Character [] a = new Character[]{'a','b','c'};
DO NOT use char, since it will compile error when type checking.

Making a 2D array of generic lists in Java

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));
}
}

Java N-Dimensional Arrays

I need to be able to have an n-dimensional field where n is based on an input to the constructor. But I'm not even sure if that's possible. Is it?
Quick solution: you could approximate it with a non-generic ArrayList of ArrayList of ... going as deep as you need to. However, this may get awkward to use pretty fast.
An alternative requiring more work could be to implement your own type using an underlying flat array representation where you calculate the indexing internally, and providing accessor methods with vararg parameters. I am not sure if it is fully workable, but may be worth a try...
Rough example (not tested, no overflow checking, error handling etc. but hopefully communicates the basic idea):
class NDimensionalArray {
private Object[] array; // internal representation of the N-dimensional array
private int[] dimensions; // dimensions of the array
private int[] multipliers; // used to calculate the index in the internal array
NDimensionalArray(int... dimensions) {
int arraySize = 1;
multipliers = new int[dimensions.length];
for (int idx = dimensions.length - 1; idx >= 0; idx--) {
multipliers[idx] = arraySize;
arraySize *= dimensions[idx];
}
array = new Object[arraySize];
this.dimensions = dimensions;
}
...
public Object get(int... indices) {
assert indices.length == dimensions.length;
int internalIndex = 0;
for (int idx = 0; idx < indices.length; idx++) {
internalIndex += indices[idx] * multipliers[idx];
}
return array[internalIndex];
}
...
}
Here's a nice article that explains how to use reflection to create arrays at run-time: Java Reflection: Arrays. That article explains how to create a one-dimensional array, but java.lang.reflect.Array also contains another newInstance method to create multi-dimensional arrays. For example:
int[] dimensions = { 10, 10, 10 }; // 3-dimensional array, 10 elements per dimension
Object myArray = Array.newInstance(String.class, dimensions); // 3D array of strings
Since the number of dimensions is not known until runtime, you can only handle the array as an Object and you must use the get and set methods of the Array class to manipulate the elements of the array.
Try this:
https://github.com/adamierymenko/hyperdrive

Java - multidimensional array of vectors of generic type

Imagine, if you will, a 10x10x10 cube made out of 1x1x1 bricks. Each brick must be accessable by an x,y,z coordinate. For each brick I also need to store a list of names of who own that 'brick'.
As efficiency is an absolute must, I came up with the following idea - A 3d array of vectors.
note- I have made a class which stores a name, and other info (called person)
//declaration
protected Vector<person>[][][] position;
I think I must then allocate memory to the pointer position. I have tried this
position = new Vector<person>[10][10][10];
But am getting an error 'Cannot create a generic array of Vector' I am only familiar with C++ and Java is new to me. I understand java does not like declaring arrays with generic type? Does anyone know how I can get around this problem?
Cheers
No need to complicate things that much! You know the size of the array (10:10:10), so there's no need to go for vectors or other stuff for the bricks. Try using array of objects:
Class Brick {
public Brick(int x, int y, int z){
this.x=x;
this.y=y;
this.z=z;
owners = new ArrayList <String> ();
}
List<String> owners;
int x, y, z; //every brick "knows" its position - you might not need it
}
Code for creating the array:
Public Class Main {
.....
Brick Cube[][][] = new Brick[10][10][10];
for (int x=0; x < 10; x++)
for(int y=0; y < 10; y++)
for(int z=0; z < 10; z++)
{
Cube[x][y][z] = new Brick(x, y, z);
}
//adding an owner to a brick:
Cube[0][0][0].owners.add("Owner");
.....
}
Keep OOP in mind - It makes things much easier!
TODO: add getters/setters
If you did want to go down the route of using a List structure rather than arrays, this should be enough to get you started. This is based off of what #FrustratedWithFormsDes said, but I included the initilization code since it's hard to get the syntax right.
public class Person {
}
class PeopleStorage {
ArrayList<ArrayList<ArrayList<Person>>> data;
public PeopleStorage(int size) {
this.data = new ArrayList<ArrayList<ArrayList<Person>>>(size);
for (int i = 0; i < size; i++) {
ArrayList<ArrayList<Person>> inner = new ArrayList<ArrayList<Person>>(
size);
data.add(inner);
for (int j = 0; j < size; j++) {
ArrayList<Person> inner2 = new ArrayList<Person>(size);
inner.add(inner2);
for (int k = 0; k < size; k++) {
inner2.add(new Person());
}
}
}
}
public Person get(int index1, int index2, int index3)
{
//check indices against size, throw IllegalArgumentException here
return data.get(index1).get(index2).get(index3);
}
public void set(Person person, int index1, int index2, int index3)
{
//check indices against size, throw IllegalArgumentException here
data.get(index1).get(index2).set(index3, person);
}
}
What you are trying to do is impossible in java, because there is no operator overloading (like in C++). In short the [] operator in Java is defined only for arrays and cannot be overloaded. Therefore the only way to access elements inside Vector/ArrayList is through the get() method (or through an Iterator or couple other methods, but you get the picture). Analogically you cannot define multi-dimensional ArrayList that way. What you should do is
ArrayList<ArrayList<ArrayList<Person>>> people = new ArrayList<ArrayList<ArrayList<Person>>>()
and then go and initialize all the internal arrays with nested loops or whatever you wish.
It looks a little bit ugly, and since ArrayList/Vector is a dynamic collection adding for example a new element at the first array would require you to initialize the two nested arrays as well. So you might be better off in design terms with writing some form of a wrapper for that class in order to isolate that logic there.
The difference between ArrayList and Vector in Java is that vector is synchronized (therefore slower) and in the default growth pattern (Vector doubles its size, while ArrayList grows by 50%). Otherwise they are pretty much identical in functionality and complexity of operations.
I think you will want to use an ArrayList instead of a vector. I'm a bit rusty on this, but from what I recall, mixing arrays and generic containers is not a good idea.
You could try this:
position = new ArrayList(10)<ArrayList(10)<ArrayList(10)<Person>>;
but it's rather ugly to read. Accessing data is like this:
person = position.get(1).get(3).get(6); //get someone at x=1, y=3, z=6
Beware I don't have the chance right now to compile this and see if it actually works, so you will want to read the docs on ArrayList but I think this is the direction you could go in...
Update: jhominal has pointed out some news that I forgot, I don't have a Java compiler on hand to verify this, but if you try this solution, take a look at what he says as well.
Note: This is more of an explanation of why generic arrays don't work in Java rather than an answer to your actual question.
Effective Java 2nd Edition deals with generic arrays on page 119 of Chapter 5 (PDF).
Why is it illegal to create a generic
array? Because it isn't typesafe. If
it were legal, casts generated by the
compiler in an otherwise correct
program could fail at runtime with a
ClassCastException. This would
violate the fundamental guarantee
provided by the generic type system.
This is because Java Generics only have types at compile time and insert the appropriate casts so specific operations work at runtime.
The easiest solution to this problem is likely the one Ed.C provided.
I would also go with the object answer by Ed.C, but this seems to work:
#SuppressWarnings("unchecked")
protected Vector<Person>[][][] position = new Vector[10][10][10];
ie, you skip the diamond on the right.

Categories