2009-04-29 18 views
5

Con el tipo Integer se puede hacer esto:Java genéricos e Infinity (comparable)

int lowest = Integer.MIN_VALUE; 

¿Qué puedo hacer yo si uso los genéricos?

K lowest = <...>; 

Necesito esto para implementar algo similar a un PriorityQueue. Tengo acceso a un nodo que deseo eliminar de la cola, pero no es el mínimo.

1. I need to make it the min by decreasing the key of that node, 
2. And then remove the min. 

Estoy atascado en el primer paso. Lo único que puedo hacer es establecer la clave del nodo al mínimo actual. No estoy seguro de que sea suficiente.

+0

¿Podría ofrecer algún código de muestra que ilustre cómo desea usar K? –

+0

¿Puedes elaborar un poco? Algún código relevante sería bueno. –

+0

Por cierto, las versiones de objeto de los literales numéricos extienden java.lang.Number. Desafortunadamente, Number no tiene nada como getMaximumValue(). – Powerlord

Respuesta

4

Esto no tiene ningún sentido ...

Dado que usted no sabe lo que K es en ese punto, (es decir, va a implementar de forma genérica ... duh!) No se puede especifique un límite mínimo/máximo para él.

en un caso donde K podría ser un int, long, string o un objeto, que no podía adivinar con sensatez utilizar

Integer.MIN_VALUE, "" o NULL.

Supongo que lo que estás buscando es un K.MIN_VALUE_OF_EVENTUAL_TYPE pero eso no existe.

+2

Puede ser alguien proveniente de un fondo de C++, donde es trivial usar solo numeric_limits :: max y numeric_limits :: min. Esto le permite escribir código que maneja caracteres de caracteres, dobles, enteros, de 64 bits o incluso clases numéricas personalizadas. – Eclipse

+0

Me tropecé con esto mientras buscaba la misma idea y aquí está mi caso de uso: 1. Tengo una interfaz con el siguiente método 'Comparable getGroupIndex (String groupName)' este método devuelve 'null' si el' groupName' es desconocido. 2. En una clase donde llamo al método anterior, si el valor devuelto es 'nulo', entonces quiero ponerlo al final de una lista (trátela como si devolviera el valor máximo de tipos comparables). En su lugar, voy a resolver esto devolviendo un valor máximo en lugar de nulo, pero solo quería arrojar algo de luz sobre un caso de uso. – mwakerman

1

¿Esto no depende de qué tipo es K?

El punto de los genéricos es que K puede ser de cualquier tipo (o cualquier subclase de un cierto tipo); para poder invocar métodos en K o acceder a sus propiedades, debe restringir sus límites de tipo con comodines.

+0

Bueno, K es comparable. –

+1

Parece una cosa válida que desear: si es un int, quiero el valor más bajo, pero si es largo, quiero el valor más bajo. Sin embargo, no estoy seguro de que haya una respuesta simple. Si no usaba Generics (solo pasa un número o un similar), probablemente podría usar el reflejo para resolver la mayoría de los casos (los números generalmente tienen el mismo campo .MIN_VALUE, creo) –

+0

¿Comparable con qué? Es solo comparable a otras K que no sabrá. –

4

No hay una forma genérica de MIN_VALUE o MAX_VALUE para todos los Tipos comparables.

Piense en una clase Time que implementa comparable. No hay MAX_VALUE para Tiempo aunque es Comparable.

1

simplemente porque un objeto es comparable no significa que tenga que tener un valor mínimo. La razón por la que int tiene un valor mínimo de - (2^(31)) es porque necesita 1 bit para un signo, por lo que 2^31 es el número entero más grande (o el más pequeño) posible de almacenar. Para cosas como la cadena, no tiene ningún sentido ya que no existe la cadena más grande o más pequeña posible, está unida a la memoria.

+0

Ciertamente hay una cadena más pequeña posible: "". Es solo el más grande que tiene memoria, – Eclipse

+0

Las reglas de clasificación son contextuales. Podría fácilmente definir un orden que ponga cadenas vacías al final. – erickson

1

Puede que tenga que crear una interfaz "IInfinity", y tener K extiende IInfinity, y IInfinity para tener un método "getInfinityValue()", y luego ajustar/extender Integer, Double, BigDecimal, etc. en una clase que implemente IInfinity ... y ugh!

1

Básicamente, usted desea que cualquier tipo K implemente algunas funciones estáticas, por ejemplo, las más bajas y las más altas que obedezcan las propiedades matemáticas estándar.

Supongo que para que este sentido de la más baja (o la más alta) se pueda utilizar, querrá que cualquier objeto comparable tenga estos métodos. (o campos estáticos). Si solo está interesado en sus propios objetos personalizados, la forma de hacerlo sería hacer que todo herede de un tipo de datos abstracto que declare los campos estáticos para MINVALUE y MAX_VALUE y luego su tipo será el de los variables.Si necesita esta funcionalidad para otras clases, necesitará crear un tipo de hashmap externo que rastree estas propiedades para diferentes clases (pero eso sería bastante feo)

4

Estoy tratando de imaginar qué escenario requeriría tal comportamiento. Esto es lo mejor que se me ocurre ...

ADVERTENCIA: Este código es peligroso. Por favor, sé misericordioso conmigo por publicar semejante abominación. Es solo una prueba de concepto.

public class Lowest<K> implements Comparable<K> { 
    public int compareTo(K other) { 
     return -1; 
    } 
} 

Y entonces ...

public class Test { 
    public <K extends Comparable<K>> K findMaximum(List<K> values) throws Exception { 
     K lowest = (K) new Lowest<K>(); /// XXX DANGER! Losing compile-time safety!!! 

     K maximum = lowest; 
     for (K value : values) { 
      if (maximum.compareTo(value) < 0) { 
       maximum = value; 
      } 
     } 

     if (maximum == lowest) { 
      throw new Exception("Could not find a maximum value"); 
     } else { 
      return maximum; 
     } 
    } 
} 
+0

Ok, esto es totalmente extraño. Hicimos el mismo comentario sobre la pregunta en un segundo el uno del otro, y luego esperamos 20 minutos y publicamos casi el mismo código en 15 segundos el uno del otro. Creo que necesito tomar un breve descanso ... –

+0

@mmyers: ¡Extraño! Creo que tomaré un largo descanso, yo mismo. :) –

+1

No estoy seguro de que es peor, sin embargo: mi uso de la reflexión para encontrar un constructor (para una clase potencialmente no trivial), o su elenco de un no K a K. –

1

no considerar hacer K un genérico, pero utilizando una interfaz que envuelve la envoltura primitiva (una doble envoltura!).

import java.util.HashMap; 


public class NodeWrapper<K extends Comparable<K>> implements Comparable<NodeWrapper<K>> { 

    private static HashMap<Class, NodeWrapper> minVals = new HashMap<Class, NodeWrapper>(); 

    private K value; 

    private NodeWrapper() { 
     super(); 
    } 

    public NodeWrapper(K value, Class<K> clazz) { 
     super(); 
     this.value = value; 

     if (minVals.get(clazz)==null) { 
      minVals.put(clazz, new NodeWrapper<K>()); 
     } 
    } 

    public K getValue() { 
     return value; 
    } 

    public static NodeWrapper getMinValue(Class clazz){ 
     return minVals.get(clazz); 
    } 

    public void setValue(K value) { 
     this.value = value; 
    } 

    @Override 
    public int compareTo(NodeWrapper<K> o) { 
     NodeWrapper min = minVals.get(this.getClass()); 
     if (this==min && o==min) { 
      return 0; 
     } else if (this==min){ 
      return -1; 
     } else if (o==min){ 
      return 1; 
     } else { 
      return this.value.compareTo(o.value); 
     } 
    } 

} 

En pocas palabras, la idea es que cada vez que se crea una instancia de una nueva clase, se crea y se pone en un mapa hash estática que almacena los valores mínimos para cada clase un valor mínimo. (De hecho, estos valores NO son NADA en absoluto, solo un objeto centinela, pero como usaremos la igualdad de objetos para determinar si algo es el valor mínimo, esto no es ningún problema). Todo lo que es necesario es que el objeto envuelto sea comparable a otras instancias de sí mismo en general.

Una desventaja es que cuando llame al getMinValue tendrá advertencias del compilador, ya que el tipo de devolución no tendrá información genérica. Puede haber una forma más elegante de evitar esto, pero no puedo pensarlo ahora.

Esta idea general puede ser bastante buena en general. Sin embargo, realmente debería insistir: esto se romperá por completo si lo prueba con cualquier polimorfismo o cualquier combinación de clases comparables entre sí. Long sy Integer s en el mismo árbol te destruirán por completo.

2

er ... ¿cuál es el problema otra vez?

PriorityQueue, como todos Collections, le permite utilizar una instancia de un objeto a remove de la colección.

+0

Haha. Enfoque :-) – KarlP

+0

Bien dicho. Recomiendo que xhaker compruebe el código fuente de la clase PriorityQueue para ver un ejemplo. –

2

Puede crear una clase contenedora que "agregue" un valor mínimo y máximo a todos los tipos. Solo tiene dos instancias estáticas que representan el mínimo y el máximo, y luego otras instancias envuelven algún otro valor de algún tipo. Cuando hacemos una comparación, verificamos si una de las cosas es el mínimo o el máximo, y devolvemos el resultado correcto; y de lo contrario solo hacemos la misma comparación que el tipo subyacente. Algo como esto:

class Extended<T extends Comparable<? super T>> implements Comparable<Extended<T>> { 
    private Extended() { } 

    private static Extended min = new Extended(); 
    private static Extended max = new Extended(); 

    @SuppressWarnings("unchecked") 
    public static <T extends Comparable<? super T>> Extended<T> getMin() { 
     return (Extended<T>)min; 
    } 
    @SuppressWarnings("unchecked") 
    public static <T extends Comparable<? super T>> Extended<T> getMax() { 
     return (Extended<T>)max; 
    } 

    public T value; 

    public Extended(T x) { value = x; } 

    public int compareTo(Extended<T> other) { 
     if (this == other) return 0; 
     else if (this == min || other == max) return -1; 
     else if (this == max || other == min) return 1; 
     else return this.value.compareTo(other.value); 
    } 
}