Comment présenter le type primitif nullable int en Java?
je conçois une classe entity qui a un champ appelé "documentYear", qui pourrait avoir des valeurs entières non signées telles que 1999, 2006, etc. Pendant ce temps, ce champ peut également être "inconnu", c'est pas sûr que l'année de création du document.
donc, une nullable int tapez comme en C# sera bien adapté. Cependant, Java n'a pas de fonctionnalité nulle comme C# A.
j'ai deux options, mais je n'aime pas tous les deux:
- Utilisation
java.lang.Integer
au lieu du type primitifint
; - utilisez -1 pour présenter la valeur" unknown"
quelqu'un a-t-il de meilleures options ou de meilleures idées?
mise à Jour: ma classe entity aura des dizaines de milliers d'occurrences; par conséquent, les frais généraux de java.lang.Entier pourrait être trop lourd pour les performances globales du système.
13 réponses
vous allez devoir soit abandonner le type primitif ou utiliser une valeur int arbitraire comme votre "année invalide".
une valeur négative est en fait un bon choix puisqu'il y a peu de chance d'avoir une année valide qui causerait un dépassement d'entier et il n'y a pas d'année négative valide.
utiliser la classe entier ici est probablement ce que vous voulez faire. Les frais généraux associés à l'objet sont très probablement (mais pas nécessairement) insignifiants pour la réactivité et la performance globale de vos applications.
des Dizaines de milliers de cas de l'Entier n'est pas beaucoup. Envisagez de dépenser quelques centaines de kilooctets plutôt que d'optimiser prématurément. C'est un petit prix à payer pour l'exactitude.
méfiez-vous des valeurs sentinelles comme null
ou 0
. Cette montants essentiellement à mentir, car 0
n'est pas une année, et null
n'est pas un entier. Une source commune de bugs, surtout si vous n'êtes pas le seul responsable du logiciel.
envisagez d'utiliser un type-safe null Option
, parfois appelé Maybe
. Populaire dans les langues comme Scala et Haskell, c'est comme un conteneur qui a un ou zéro éléments. Votre champ aurait le type Option<Integer>
, qui annonce la nature optionnelle de votre champ de l'année dans le système de type et force les autres codes à traiter les années manquantes.
Voici une bibliothèque qui inclut l'Option type.
Voici comment vous appelleriez votre code si vous utilisiez c':
partyLikeIts.setDocumentYear(Option.some(1999));
Option<Integer> y = doc.getDocumentYear();
if (y.isSome())
// This doc has a year
else
// This doc has no year
for (Integer year: y) {
// This code only executed if the document has a year.
}
une Autre option est d'avoir un associé boolean
indicateur qui indique si la valeur de votre année est valide ou non. Ce drapeau a été false
signifierait que l'année est "inconnue."Cela signifie que vous devez cocher une primitive (booléenne) pour savoir si vous avez une valeur, et si vous le faites, cochez une autre primitive (entière).
les valeurs sentinelles conduisent souvent à un code fragile, donc il vaut la peine de faire l'effort d'éviter la valeur sentinelle sauf si vous êtes très sûr qu'il ne sera jamais une utilisation cas.
vous pouvez utiliser un int régulier, mais utilisez une valeur comme Integer.MAX_VALUE
ou Integer.MIN_VALUE
qui sont des constantes définies comme votre date non valide. Il est également plus évident que -1 ou une faible valeur négative qu'il est invalide, il ne ressemblera certainement pas à une date à 4 chiffres que nous sommes habitués à voir.
si vous avez un entier et que vous craignez qu'une valeur arbitraire pour null puisse être confondue avec une valeur réelle, vous pouvez utiliser long à la place. Il est plus efficace que d'utiliser un entier et Long.MIN_VALUE n'est pas à proximité d'une valeur int valide.
pour être complet, une autre option (certainement pas la plus efficace), est d'utiliser une classe de wrapper Year
.
class Year {
public int year;
public Year(int year) { this.year = year; }
}
Year documentYear = null;
documentYear = new Year(2013);
Ou, si elle est plus sémantique, ou vous souhaitez que plusieurs types de nullable ints (Autres que des Années), vous pouvez imiter C# Nullable primitives comme suit:
class Int {
public int value;
public Int(int value) { this.value = value; }
@Override
public String toString() { return value; }
}
int primitif vs le Entier le type est un exemple parfait d'optimisation prématurée.
Si vous faites le calcul:
- int = N (4)
- Entier = N (16)
donc pour 10,000 ints ça coûtera 40,000 octets ou 40k. Pour 10 000 entrées, ça coûtera 160 000 octets ou 160K. Si vous considérez la quantité de mémoire nécessaire pour traiter les images/photos/données vidéo qui est pratiquement négligeable.
mon la suggestion est, arrêter de perdre du temps prématurément l'optimisation basée sur les types de variables et de chercher une bonne structure de données qui rendra facile à traiter toutes ces données. Peu importe ce que vous faites, à moins que vous définissiez des variables primitives 10K individuellement, ça va finir sur le tas de toute façon.
Qu'est-ce qui ne va pas avec java?lang.Entier? C'est une solution raisonnable, sauf si vous stockez de très grandes quantités de cette valeur peut-être.
si vous voulez utiliser des primitives, une valeur -1 serait également une bonne solution. La seule autre option que vous avez est d'utiliser un drapeau booléen séparé, comme quelqu'un l'a déjà suggéré. Choisissez votre poison :)
PS: bon sang, j'essayais de m'en tirer avec un petit mensonge blanc sur les objets vs les structures. Mon point est qu'il utilise plus de mémoire, semblable à la méthode du drapeau booléen, bien que syntaxiquement le type nul soit plus agréable bien sûr. En outre, je n'étais pas sûr que quelqu'un avec un fond Java saurait ce que je voulais dire avec un struct.
java.lang.Entier est raisonnable pour ce cas. Et il a déjà mis en œuvre Serialisable, de sorte que vous pouvez enregistrer seulement le champ de l'année jusqu'au HDD et le charger en arrière.
une autre option pourrait être d'utiliser une valeur spéciale en interne (-1 ou entier.MIN_VALUE ou similaire), mais exposez l'entier comme deux méthodes:
hasValue() {
return (internalValue != -1);
}
getValue() {
if (internalValue == -1) {
throw new IllegalStateException(
"Check hasValue() before calling getValue().");
}
return internalValue;
}
si vous voulez sauver la mémoire, je vous conseille d'emballer plusieurs années en une seule int
. Ainsi,0
nil
. Ensuite, vous pouvez faire des hypothèses afin d'optimiser. Si vous travaillez seulement avec les dates actuelles, comme les années 1970-2014, vous pouvez soustraire 1969 de tous les et entrer dans 1—55
plage. Ces valeurs peuvent être codées avec seulement 6 bits. Ainsi vous pouvez diviser votre int
qui est toujours 32 bits, en 4 zones, avec un an là-dedans. De cette façon, vous pouvez pack 4 ans dans la gamme 1970-2226 en un single int
. Plus votre gamme est étroite, comme seulement 2000-2014( 4 bits), plus d'années vous pouvez emballer dans un seul int
.
vous pouvez utiliser l'annotation @Nullable si vous utilisez java 7