2009-05-18 28 views
18

En un intento de ver si puedo limpiar algunos de mis códigos matemáticos, en su mayoría elementos de matriz, estoy tratando de usar algunos Java Generics. Tengo el siguiente método:Genéricos y números de Java

private <T> T[][] zeroMatrix(int row, int col) { 
    T[][] retVal = (T[][])new Object[row][col]; 
    for(int i = row; i < row; i++) { 
     for(int j = col; j < col; j++) { 
      retVal[i][j] = 0; 
     } 
    } 
    return retVal; 
} 

La línea RetVal [i] [j] = 0 me está causando el uno dolores de cabeza. El objetivo de la línea es para inicializar la matriz con la representación de T 0. He intentado hacer todo tipo de cosas con él: (T se define en la clase como T se extiende Número)

retVal[i][j] = (T)0; 
retVal[i][j] = new T(0); 

la Lo único que funciona es

retVal[i][j] = (T)new Object(0); 

Que no es lo que quiero.

¿Esto es posible? ¿Existe alguna manera más fácil de representar una matriz NxM de cualquier tipo de Número (incluido potencialmente BigDecimal), o estoy atascado?

+1

No tengo mucho que agregar a esta discusión, pero gracias por proporcionarme una pregunta que provocó tan buenas respuestas. No estoy contento con la implementación de genéricos de Java, pero ahora los entiendo mejor. –

Respuesta

1

Las matrices de Ye olde (de referencia) no funcionan bien con los genéricos. En este caso, las matrices también son ineficientes. Está creando una matriz de matrices, por lo que hay una indirección y una comprobación de límites innecesarias. Mejor hacer una clase Matrix<T>. Es posible que también desee agregar al Matrix una referencia a una instancia de T que representa un cero.

+1

Esto es realmente código de mi clase Matrix . –

+0

Sugiero usar Number [] o List como el tipo "array". java.util simplemente se pone en un lío al escribirse con matrices genéricas falsificadas. Necesitará un método como ('private Number [] [] zeroMatrix (int row, int col, Number zero)' o 'private List zeroMatrix (int row, int col, T zero)'). –

1

Los genéricos y matrices no coinciden muy bien. No se permite crear una matriz genérica, ya que no sería segura. Se deriva del hecho de que si Sub es un subtipo de Super, entonces Sub [] es un subtipo de Super [], que no es el caso de los tipos genéricos; para dos tipos distintos, Type1 y Type2, List no es un subtipo o un supertipo de List. (Java efectivo lo cubre en el capítulo 5, elemento 25).

-1

El uso de Java de borrado para implementar genéricos significa que tendrá problemas para encontrar un tipo genérico nuevo.

Cómo sobre el uso nula para representar 0

retVal[i][j] = null; 

continuación, puede asignarle el tipo que desee a la matriz más adelante.

+0

No me gusta esta solución ya que null no es cero en Java – dfa

2

En Java, el tipo se borra en el tiempo de ejecución, por lo que debe pasar otro argumento para obtener el tipo en tiempo de ejecución.

Ese podría ser el valor para inicializar las matrices o la clase que se usará.

Si elige pasar la clase, entonces tenga un Mapa de clase a valor para almacenar un valor cero para cada tipo.

A continuación, puede utilizar java.util.Arrays.fill para llenar la matriz:

private static HashMap<Class<?>, Object> ZEROS = new HashMap<Class<?>,Object>(); 

static { 
    ZEROS.put(Integer.class, Integer.valueOf(0)); 
    ... 
} 

private static <T extends Number> T[][] zeroMatrix (Class<T> type, int rows, int cols) { 
    @SuppressWarnings("unchecked") 
    T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(type, rows, cols); 
    Object zero = ZEROS.get(type); 

    for (T[] row : matrix) 
     java.util.Arrays.fill(row,zero); 

    return matrix; 
} 

Integer[][] matrix = zeroMatrix (Integer.class, 10, 10); 

Sin embargo, si el rendimiento es de forma remota una preocupación que no quiere ser el uso de valores en caja para el código numérico.

Realmente no quiere probar y usar nulo para cero - triplicará la complejidad de todas las demás rutas en su código. Aunque podría salirse con la suya con una clase de soporte numérico que proporcionaría la suma y multiplicación de los diversos tipos de números encuadrados, la cantidad de complejidad que ahorrará será muy pequeña en comparación con proporcionar dos o tres matrices primitivas y un par de números grandes, especialmente si usa un sistema de plantillas (por ejemplo, una tarea de reemplazo de hormigas o XSLT) para generar el código fuente.

+0

@Bill en tiempo de compilación, el tipo genérico está disponible para comprobación de tipos; por lo tanto, no se borra en tiempo de compilación, sino después de la compilación. El compilador lo borra del código pasado a la JVM, por lo tanto, en el tiempo de ejecución, se borra. –

1

Creo que estás luchando una batalla perdida. Incluso si resuelves esto, ¿cómo planeas resolver la suma, la resta, etc.? La clase numérica no es una superclase muy útil, y el único método útil es doubleValue().

Cero se puede definir como la identidad, además, o un cero en la multiplicación, pero sin una definición genérica de adición o multiplicación, una definición genérica de cero es poco probable.

Si desea esto, es mejor que se quede con BigDecimal para todo, pero por supuesto tendrá penalizaciones de rendimiento asociadas.

La otra opción obvia sería dejar la matriz con una inicialización nula, y luego cambiar el otro código para tratar nulo como cero.

3

debe ser nulo en lugar de cero.

Si quiere poner realmente allí el equivalente 0 para el objeto T se tendrá que proporcionar una fábrica de T. Algo como esto:

interface Factory<T> { 
    T getZero();  
} 

y usted debe hacer que el método como este:

private <T> T[][] zeroMatrix(int row, int col, Factory<T> factory) { 
    T[][] retVal = (T[][])new Object[row][col]; 
    for(int i = row; i < row; i++) { 
     for(int j = col; j < col; j++) { 
      retVal[i][j] = factory.getZero(); 
     } 
    } 

    return retVal; 
} 

también debe tener implementaciones adecuadas para la fábrica:

class IntegerFactory implements Factory<Integer> { 
    Integer getZero() { 
     return new Integer(0); 
    } 
} 

Tampoco Mally pondría el getMatrix(int row, int column) en la implementación de fábrica también para devolver realmente una matriz tipada adecuada.

+0

Esto se rompe en tiempo de ejecución obviamente. No puede convertir un Objeto [] [] en Integet [] []. –

+0

Intenta agregar límites de plantilla, p.

+0

getZero() debe almacenar en caché o simplemente usar Integer.valueOf() – dfa

1

Debe tener en cuenta que los genéricos solo se utilizan en tiempo de compilación para la verificación de seguridad de tipo. Esta información se pierde durante el tiempo de ejecución, por lo que no puede usar el auto-boxing en retVal [i] [j] = 0; ya que Java no puede autoencastrar para escribir Number u Object.

Si pasa el valor que desea establecer, funcionará. He aquí una muestra rápida:

private <T> T[][] fillMatrix(int row, int col, T value) { 
    T[][] retVal = (T[][])new Object[row][col]; 
    for(int i = 0; i < row; i++) { 
     for(int j = 0; j < col; j++) { 
      retVal[i][j] = value; 
     } 
    } 
    return retVal; 
} 

Por cierto, for (int i = fila; i < fila; i ++) y para (int j = col; j < col; j ++) nunca se bucle de modo que hay otro problema con su código.

edit: No podrá convertir el resultado en algo más que Object [] [] porque ese es el tipo de matriz real.

+0

Quizás un << para (T [] t1: retVal) para (T t2: t1) t2 = valor; >> podría funcionar. – ATorras

+2

aumenta: java.lang.ClassCastException: [[Ljava.lang.Object; no se puede convertir a [[Ljava.lang.Integer; – dfa

+0

+1 ¡Ay! Tiene razón – ATorras

12
<T extends Number> T[][] zeroMatrix(Class<? extends Number> of, int row, int col) { 
    T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(of, row, col); 
    T zero = (T) of.getConstructor(String.class).newInstance("0"); 
    // not handling exception  

    for (int i = 0; i < row; i++) { 
     for (int j = 0; j < col; 
      matrix[i][j] = zero; 
     } 
    } 

    return matrix; 
} 

de uso:

BigInteger[][] bigIntegerMatrix = zeroMatrix(BigInteger.class, 3, 3); 
    Integer[][] integerMatrix = zeroMatrix(Integer.class, 3, 3); 
    Float[][] floatMatrix = zeroMatrix(Float.class, 3, 3); 
    String[][] error = zeroMatrix(String.class, 3, 3); // <--- compile time error 
    System.out.println(Arrays.deepToString(bigIntegerMatrix)); 
    System.out.println(Arrays.deepToString(integerMatrix)); 
    System.out.println(Arrays.deepToString(floatMatrix)); 

EDITAR

una matriz genérica:

public static <T> T[][] fillMatrix(Object fill, int row, int col) { 
    T[][] matrix = (T[][]) Array.newInstance(fill.getClass(), row, col); 

    for (int i = 0; i < row; i++) { 
     for (int j = 0; j < col; j++) { 
      matrix[i][j] = (T) fill; 
     } 
    } 

    return matrix; 
}  

Integer[][] zeroMatrix = fillMatrix(0, 3, 3); // a zero-filled 3x3 matrix 
String[][] stringMatrix = fillMatrix("B", 2, 2); // a B-filled 2x2 matrix 
+0

que suena bien, no conozco los genéricos lo suficiente como para decirlo con certeza, pero si funciona bien, entonces debe ser correcto. :) –

+0

Umm ... espera un minuto. No tengo problemas con la corrección, pero tengo un problema con la eficiencia. Debería poner la nuevaInstancia ("0") al comienzo, p. T cero = .... nuevaInstancia ("0"), y asignar matriz [i] [j] = cero en el ciclo. –

+0

(solo problemático si T es una clase mutable y no creo que sean) –

8

Arrays y Generics no juegan bien juntos:

"Las matrices son covariantes, lo que significa que una matriz de referencias de supertipo es un supertipo de una matriz de referencias de subtipo.Es decir, Object[] es un supertipo de String[] y una matriz de cadenas se puede acceder a través de una variable de referencia de tipo Object[] "

ver la Java Generics FAQ:.

1

Si realmente desea usar medicamentos genéricos, puede hacer algo como esto

private <T extends Number> T[][] zeroMatrix(int row, int col, Class<T> clazz) throws InstantiationException, IllegalAccessException, 
     IllegalArgumentException, InvocationTargetException 
{ 
    T[][] retVal = (T[][]) Array.newInstance(clazz, new int[] { row, col }); 
    for (int i = 0; i < row; i++) 
    { 
     for (int j = 0; j < col; j++) 
     { 
      Constructor<T> c = clazz.getDeclaredConstructors()[0]; 
      retVal[i][j] = c.newInstance("0"); 
     } 
    } 

    return retVal; 
} 

Ejemplo:

zeroMatrix(12, 12, Integer.class); 
1

Me levantó una related question which also asked about performance issues que hacía referencia a su pregunta. El consenso fue claro en cuanto a que la refactorización a Generics tuvo un impacto considerable en el rendimiento y, por lo tanto, debería quedarse con los primitivos si esto es importante (para mí lo es).