Как представить нулевой примитивный тип int в Java?

Я разрабатываю класс сущности, который имеет поле с именем «documentYear», которое может иметь целочисленные значения без знака, такие как 1999, 2006 и т. д. Между тем, это поле также может быть «неизвестным», то есть не уверен, в каком году документ созданный.

Следовательно, тип int, допускающий значение NULL, как в C#, будет хорошо подходить. Однако в Java нет функции, допускающей значение NULL, как в C#.

У меня есть два варианта, но они оба мне не нравятся:

  1. Используйте java.lang.Integer вместо примитивного типа int;
  2. Используйте -1, чтобы представить «неизвестное» значение

У кого-нибудь есть лучшие варианты или идеи?

Обновление: мой класс сущностей будет иметь десятки тысяч экземпляров; поэтому накладные расходы java.lang.Integer могут быть слишком большими для общей производительности системы.


person yinyueyouge    schedule 12.06.2009    source источник


Ответы (13)


Вам придется либо отказаться от примитивного типа, либо использовать какое-то произвольное значение int в качестве вашего «недопустимого года».

Отрицательное значение на самом деле является хорошим выбором, так как маловероятно, что допустимый год будет вызывать целочисленное переполнение, а допустимого отрицательного года нет.

person hhafez    schedule 12.06.2009
comment
... если вы не хотите иметь возможность представлять годы до нашей эры и года нашей эры с помощью схемы. ;-) - person Anders Lindahl; 12.06.2009
comment
Используйте 0. Нет нулевого года, до нашей эры или нашей эры. - person Zac Thompson; 10.11.2010
comment
Если ваш домен не является астрономическим, где год 0 действителен. - person Chadwick; 24.04.2012
comment
Год 2147483647 также маловероятен и может использоваться как произвольное значение int. - person Simon Forsberg; 20.08.2012
comment
@SimonAndréForsberg - Конечно, это то, что они говорили о 2000 году. - person Nobody; 27.08.2012
comment
Что ж, шансы на то, что программа станет устаревшей и будет удалена из-за отсутствия использования к 2147483647 году, действительно велики, Саймон совершенно прав. - person Felype; 01.12.2015
comment
До тех пор, пока не будут изобретены путешествия во времени, что, я уверен, звучит для нас сейчас так же нелепо, как дешевые жесткие диски емкостью 1 ТБ казались программистам 30 лет назад. - person sh4d0w; 09.02.2016
comment
Категорически не согласен. Это нарушает единую ответственность и может привести к ошибкам, поскольку вы выражаете несколько (2) проблем с одним значением. Вы действительно должны изобрести тип, который имеет возможность выражать отсутствие значения. И нет, значение null примитива в штучной упаковке по-прежнему выражает две проблемы в одном значении. - person Noel Widmer; 16.11.2017

Использование класса Integer здесь, вероятно, то, что вы хотите сделать. Накладные расходы, связанные с объектом, скорее всего (хотя и не обязательно) тривиальны для общего отклика и производительности ваших приложений.

person Matthew Vines    schedule 12.06.2009

Десятки тысяч экземпляров Integer — это немного. Подумайте о том, чтобы потратить несколько сотен килобайт, а не оптимизировать преждевременно. Это небольшая цена за правильность.

Остерегайтесь использования контрольных значений, таких как null или 0. По сути, это равносильно лжи, поскольку 0 — это не год, а null — не целое число. Распространенный источник ошибок, особенно если вы в какой-то момент не являетесь единственным специалистом по сопровождению программного обеспечения.

Рассмотрите возможность использования безопасного типа null, такого как Option, иногда известного как Maybe. Популярный в таких языках, как Scala и Haskell, он похож на контейнер с одним или нулевым элементом. Ваше поле будет иметь тип Option<Integer>, который объявляет необязательный характер вашего поля года системе типов и заставляет другой код иметь дело с возможными отсутствующими годами.

Вот библиотека, включающая тип Option.

Вот как бы вы назвали свой код, если бы использовали его:

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.
}
person Apocalisp    schedule 12.06.2009
comment
Как -1 не год? 1 г. до н.э. - это год, и вы должны записать это как -1 как целое число. Я бы сказал, что Integer.MAX_VALUE, вероятно, не является реалистичным значением для года в течение достаточно долгого времени. - person elmuerte; 12.06.2009
comment
Вы правы, это 0, это не год. Но дело не в этом. Дело в том, что сторожевые значения теряют смысл и побеждают систему типов. - person Apocalisp; 12.06.2009
comment
Я согласен с точкой зрения Апокалиспа о дозорных ценностях. Интерпретация «-1» как значения ошибки должна быть задокументирована. Программист может непреднамеренно использовать значения ошибок в коде. Программа будет демонстрировать «мусор на входе, мусор на выходе». Однако использование типа Option — это механический способ заставить программистов задуматься о возможности неверного года. - person fatuhoku; 20.01.2012
comment
Сегодня я понял, что Integer в Java и int? в C# ведут себя по-разному по сравнению с необнуляемым. C# выполняет сравнение, но Java генерирует исключение NullPointerException. Так что Option‹Integer› — хороший выбор. - person Simon Forsberg; 01.02.2013

Другой вариант — иметь связанный флаг boolean, который указывает, действительно ли ваше значение года. Этот флаг false будет означать, что год «неизвестен». Это означает, что вам нужно проверить один примитив (логическое значение), чтобы узнать, есть ли у вас значение, и если да, то проверить другой примитив (целое число).

Значения Sentinel часто приводят к хрупкому коду, поэтому стоит приложить усилия, чтобы избежать значения Sentinel, если только вы не уверены, что оно никогда не будет использоваться.

person Eddie    schedule 12.06.2009
comment
Это не будет примитивным типом - person hhafez; 12.06.2009
comment
Думайте нестандартно. Если вы не будете использовать объект и настаиваете на использовании примитивного типа для хранения значения, то самый безопасный способ узнать, имеет ли этот примитивный тип значение, — это иметь связанное логическое значение, которое говорит вам, является ли примитивный тип или нет. содержит значение или нет. Все остальное, что вы делаете, зависит от использования контрольного значения, которое часто рискованно и приводит к хрупкому коду. - person Eddie; 25.02.2012

Вы можете использовать обычное целое число, но используйте значение, такое как Integer.MAX_VALUE или Integer.MIN_VALUE, которые являются определенными константами в качестве вашей недопустимой даты. Также более очевидно, что -1 или низкое отрицательное значение, что оно недействительно, оно точно не будет выглядеть как 4-значная дата, которую мы привыкли видеть.

person Kekoa    schedule 12.06.2009

Если у вас есть целое число и вы обеспокоены тем, что произвольное значение null может быть перепутано с реальным значением, вместо этого вы можете использовать long. Это более эффективно, чем использование Integer, а Long.MIN_VALUE не имеет ничего общего с допустимым значением int.

person Peter Lawrey    schedule 12.06.2009

Для полноты другой вариант (определенно не самый эффективный) — использовать класс-оболочку Year.

class Year {
    public int year;
    public Year(int year) { this.year = year; }
}

Year documentYear = null;
documentYear = new Year(2013);

Или, если это более семантично, или вам нужны несколько типов целых чисел, допускающих значение NULL (кроме Years), вы можете имитировать примитивы C# Nullable следующим образом:

class Int {
    public int value;
    public Int(int value) { this.value = value; }
    @Override 
    public String toString() { return value; }
}
person azz    schedule 15.04.2013
comment
О, и, что немаловажно, в Java 8 будет java.time.Year Класс - person azz; 15.04.2013

Использование примитива int вместо типа Integer — прекрасный пример преждевременной оптимизации.

Если вы сделаете математику:

  • интервал = N (4)
  • Целое число = N(16)

Таким образом, для 10 000 целых чисел это будет стоить 40 000 байт или 40 КБ. Для 10 000 целых это будет стоить 160 000 байт или 160 КБ. Если учесть объем памяти, необходимый для обработки изображений/фото/видеоданных, это практически ничтожно мало.

Мое предложение: перестаньте тратить время на преждевременную оптимизацию на основе типов переменных и ищите хорошую структуру данных, которая упростит обработку всех этих данных. Независимо от того, что вы делаете, если вы не определите 10K примитивных переменных по отдельности, они все равно окажутся в куче.

person Evan Plaice    schedule 22.02.2014

Что не так с java.lang.Integer? Это разумное решение, если вы, возможно, не храните очень большие суммы этого значения.

Если вы хотите использовать примитивы, значение -1 также будет хорошим решением. Единственный другой вариант, который у вас есть, - это использовать отдельный логический флаг, как кто-то уже предложил. Выбери свой яд :)

PS: черт вас побери, я пытался уйти с небольшой белой ложью об объектах и ​​структурах. Моя точка зрения заключалась в том, что он использует больше памяти, подобно методу логического флага, хотя синтаксически тип с нулевым значением, конечно, лучше. Кроме того, я не был уверен, что кто-то со знанием Java поймет, что я имел в виду под struct.

person Thorarin    schedule 12.06.2009
comment
Типы с нулевым значением в С# являются структурами. Они не в коробке. - person Matthew Vines; 12.06.2009
comment
Значения Nullable не упакованы в С#, как я знаю. Поправьте меня, если я ошибаюсь. - person yinyueyouge; 12.06.2009
comment
Это действительно распространенное заблуждение. Я сам был виноват в этом до недавнего времени. Но проверьте msdn.microsoft.com/en-us/library/1t3y8s4s.aspx Первое предложение: Nullable-типы являются экземплярами структуры System.Nullable‹T›. - person Matthew Vines; 12.06.2009

java.lang.Integer подходит для этого случая. И он уже реализовал Serializable, поэтому вы можете сохранить только поле года на жесткий диск и загрузить его обратно.

person Truong Ha    schedule 23.07.2010

Другой вариант может состоять в том, чтобы использовать специальное значение внутри (-1 или Integer.MIN_VALUE или подобное), но предоставлять целое число как два метода:

hasValue() {
    return (internalValue != -1);
}

getValue() {
    if (internalValue == -1) {
        throw new IllegalStateException(
            "Check hasValue() before calling getValue().");
    }
    return internalValue;
}
person MB.    schedule 22.11.2011

Если вы собираетесь экономить память, я бы посоветовал упаковать несколько лет в один int. Таким образом, 0 есть nil. Затем вы можете сделать предположения для оптимизации. Если вы работаете только с текущими датами, например, с 1970 по 2014 годы, вы можете вычесть из них 1969 год и получить диапазон 1—55. Такие значения могут быть закодированы только 6 битами. Таким образом, вы можете разделить свой int, который всегда 32-битный, на 4 зоны с указанием года в них. Таким образом, вы можете упаковать 4 года в диапазоне 1970–2226 годов в один int. Чем уже ваш диапазон, например только 2000—2014 (4 бита), тем больше лет вы сможете уместить в одном int.

person Aleks N.    schedule 22.02.2014

Вы можете использовать аннотацию @Nullable при использовании java 7

person user3231931    schedule 07.01.2015