For now input is restricted to the form of ArrayList<ArrayList<Integer>>
or ArrayList<ArrayList<Double>>
. I understand that ArrayList<Integer>
is not a subclass of ArrayList<Number>
, and when I try to use ArrayList<?>
or ArrayList<? extends Number>
the other known errors occur with adding (namely that they're not superclasses either AND the runtime-adding issue). Yes, I could just make two copies of the method, one for ArrayList<ArrayList<Integer>>
and one for ArrayList<ArrayList<Double>>
, but that's ugly for other reasons. Similarly, converting to arrays or making my own custom matrix class.
I've seen lots of posts on this problem, but no solutions anywhere except to rewrite large sections of code. Is there any simple answer in Java for how to write a method that can accept both of those as input?
I've tried everything I can think of. That's why I'm here. I can't even create a helper method to break out the inner ArrayLists, since that method would itself have to accept one of the aforementioned inputs.
The current code, if it helps:
static String integerMatrixToString(ArrayList<ArrayList<Integer>> matrix) {
ArrayList<String> s = new ArrayList<>();
for (int j = 0; j < matrix.get(0).size(); j++) {
ArrayList<Integer> n = new ArrayList<>();
for (int i = 0; i < matrix.size(); i++) {
n.add(matrix.get(i).get(j));
}
s.add(myArrayListToString(n, ParseType.EMPTY_EMPTY_SPACE));
}
return myArrayListToString(s, ParseType.EMPTY_EMPTY_NEWLINE);
}
You can solve this by making the method generic, with a type parameter N extends Numeric
. Here is a complete example:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class NumberFormatter {
public static void main(String[] args) {
List<List<Integer>> intMatrix = List.of(
List.of(1, 2),
List.of(3, 4)
);
/*
* 1 3
* 2 4
*/
List<List<Double>> doubleMatrix = List.of(
List.of(5.6, 7.8),
List.of(9.0, 1.2)
);
/*
* 5.6 9.0
* 7.8 1.2
*/
System.out.println(matrixToString(intMatrix));
System.out.println(matrixToString(doubleMatrix));
}
static <N> String matrixToString(List<? extends List<N>> matrix) {
List<String> s = new ArrayList<>();
for (int j = 0; j < matrix.get(0).size(); j++) {
List<N> n = new ArrayList<>();
for (int i = 0; i < matrix.size(); i++) {
n.add(matrix.get(i).get(j));
}
s.add(myArrayListToString(n, ParseType.EMPTY_EMPTY_SPACE));
}
return myArrayListToString(s, ParseType.EMPTY_EMPTY_NEWLINE);
}
private static String myArrayListToString(List<?> nums, ParseType parseType) {
return nums.stream()
.map(Object::toString)
.collect(Collectors.joining(parseType.delimiter));
}
private enum ParseType {
EMPTY_EMPTY_NEWLINE("\n"), EMPTY_EMPTY_SPACE(" ");
private final String delimiter;
private ParseType(String delimiter) {
this.delimiter = delimiter;
}
}
}
Note that the behaviour of transposing the matrix in the output was in the original code provided, so I assumed that this is intentional and preserved this behaviour.
Also, there's nothing in this implementation of matrixToString
that actually requires the data in the matrix to be numeric, as the only method called on each cell is Object.toString()
. If you wish to allow any type of data in the matrix, simply remove extends Number
from the type parameter, and the method will have the same behaviour for numeric types while also allowing other types.