0,0
рейтинг
11 ноября 2011 в 16:53

Разработка → Маленькие хитрости Java. Часть 2

JAVA*
В продолжение первой статьи я добавлю еще несколько штрихов о наиболее часто встречающихся ошибках и просто плохом коде, с которым часто приходится иметь дело при работе с уже написанными проектами. Я не выносил это в первую часть, так как эти ситуации встречаются гораздо реже, но поскольку первая часть вызвала много позитивных отзывов, решил продолжить. Спасибо всем комментаторам, отзывам и замечаниям. Я постараюсь избежать допущенных ошибок. Итак, продолжим:

Buffered Streams

//медленно
InputStream is = new FileInputStream(file);
int val;
while ((val = is.read()) != -1) {
}
//быстро
InputStream is = new BufferedInputStream(new FileInputStream(file));
int val;
while ((val = is.read()) != -1) {
}

Казалось бы — очевидная истина, неправда ли? Но как показал чужой код и опыт собеседования кандидатов, часть разработчиков определенно не понимает в чем преимущество буферизованных стримов. Кто до сих пор не разобрался — метод read() класса FileInputStream:
public native int read() throws IOException;

Согласитесь, каждый раз делать системный вызов, чтобы считать один байт несколько расточительно. Собственно для того, чтобы избежать этой проблемы и были созданы оболочки-буферы. Все что они делают — при первом вызове системного read() считывают несколько больше (в зависимости от указанного размера буфера, котрый по умолчанию равен 8 кб) и при следующем вызове read() считывают данные уже из буфера. Прирост производительности — на порядок. Системные вызовы, на самом деле, это не всегда плохо, например:
System.arraycopy(src, srcPos, dest, destPos, length);

В случае копированния массива — системный метод будет гораздо быстрей реализованного на java. И еще — считывайте данные порциями, а не по байтам, это тоже позволит прилично сэкономить.

Enum vs String

//медленно
String status = plan.getStatus();
if (status.equals("draft")) {
//do stuff
} else if (status.equals("submitted")) {
//do stuff
}
//быстро
PlanStatus status = plan.getStatus();
if (status == PlanStatus.DRAFT) {
//do stuff
} else if (status == PlanStatus.SUBMITTED) {
//do stuff
}

Используйте Enum для предопределенных состояний модели там, где это возможно. Это не только упрощает понимание кода, но и ускоряет процесс сравнения этих состояний. Сравнение по ссылке, определенно, на порядок быстрее стравнения строк или других обьектов. Правда, в этом случае есть один большой минус — стоимость поддержки приложения увеличивается, особенно это становится заметным, когда нужно добавить, удалить или изменить одно из существующих состояний. Но в случае если это неизменные свойства, как месяц года, то смело используйте перечисление.

ArrayList vs HashSet

Очень внимательно подходите к вопросу хранения данных в коллекциях. Очень часто Set используется там, где можно обойтись списком, а список там, где можна обойтись массивом. Например в классах, которые мапятся на таблицы баз данных. Избегайте этого, стройте правильные зависимости и корректные запросы. Это позволит секономить Вам немного памяти, ведь не для кого не секрет, что HashSet реализован на HashMap, а HashMap в свою очередь использует внутренний класс Entry, который помимо самого ключа и значения хранит также хеш и ссылку на следующий обьект Entry. В дополнение — постоянно при добавлении вычисляется хеш, который для сложных обьектов может стоить дороговато.

Data reading

//плохо
byte[] fileBinaryData = readFile(filepath);
//получше
InputStream fileStream = readFile(filepath);

Что плохого в этих невинных строках? Да в общем-то ничего, кроме того факта, что мы не знаем размер файла который считываем. Вместо файла может быть что угодно — открытый сокет, который принимает данные, данные из post запроса, все… В общем случае старайтесь не считывать всё в массив байтов, у вас попросту может не хватить памяти. Поэтому будьте осторожны с размером любых данных. Старайтесь обрабатывать и пересылать данные по частям. И всегда следите за размером пользовательских данных и по возможности ограничивайте их.

Log field

//плохо
private Logger logger = Logger.getLogger(Plan.class);
//хорошо
private static final Logger logger = Logger.getLogger(Plan.class);

Используете логированние в классе? Всегда определяйте переменную лога как static final. Во-первых: у вас никогда не возникнет проблем при попытках сериализации и десериализации обьекта. Во-вторых: инициализация происходит только 1 раз, вместо постоянной инициализации при создании обьектов класса.

Fields initialization

//плохо
class Plan {
	private String name = null;
	private Boolean isNew = false;
	private Set<Document> documents = new HashSet<Document>();
}
//хорошо
class Plan {
	private String name;
	private Boolean isNew;
	private Set<Document> documents;
}

Старайтесь не инициализировать поля класса преждевременно, кроме случаев когда вы в этом очень сильно уверены. Всё-таки для этих целей у Вас есть конструктор. В случае явного указания значений для полей класса — они будут проинициализированы до вызова конструктора, что влечет дополнительные расходы в тех случаях, когда у Вас нет необходимости пользоваться проинициализированными значениями, например, когда сразу после создания обьекта Вы устанавливаете другие значения для проинициализированных полей. Ну и не забывайте, что поля класса всегда инициализируются значениями по умолчанию.

Empty String

//медленно
if (name.equals("")) {
//быстро
if (name.isEmpty()) {

Если вам нужно проверить, содержит ли строка пустое значение — используйте метод isEmpty(). Почему не equals()? Он банально медленней. Если вы просмотрите его реализацию для строки, то сразу все поймете. Не удивляйтесь, многие разработчики до сих пор не знают про этот метод.

Object[] vs custom Class

//плохо
Object[] data = new Object[3];
data[0] = row.getUserCount();
data[1] = row.getOwnerCount();
data[2] = row.getLocations();
return data;
//получше
RowResult data = new RowResult();
data.setUserCount(row.getUserCount());
data.setOwnerCount(row.getOwnerCount());
data.setLocations(row.getLocations());
return data;

Код с Object абсолютно не читабельный, когда приходится работать с возвращаемым значением в другом месте. Тоесть, чтобы понять что же хранит в себе возвращаемое значение — нужно вернутся в клас вызова и читать комментарии к методу, если же, конечно, они есть, а если нету вникать в код, что не очень эффективно. Собственно, я сам этим иногда грешу, когда очень спешу. С этим можна смирится, если это очень редко, но, все же, в таких ситуациях лучше создавать новый класс. Облегчите жизнь тем, кто прийдет после Вас.

Anonymous Classes

Аннонимные классы — это отличная фича, но не пихайте их во все места Вашего приложения только потому, что это сэкономит Вам минуту времени. Код с анонимными классами читается гораздо трудней и сложен для восприятия. Лучше же, конечно, выносить в отдельные классы, кроме случаев, когда аннонимный класс очень уместен.

Comparison order

//плохо
if (name.equals("broadcast")) {
//хорошо
if ("broadcast".equals(name)) {

Этот способ позволяет избавится от лишней null проверки. Но если придерживаться правил из первой статьи, то в этом способе нету необходимости.

Выводы

Да, многое из написанного — очевиднее очевидного. Но если так, то откуда же это все берется от проекта к проекту? Эта статья ни в коем случае не пособие по улучшению производительности вашего приложения. Это просто хороший, правильный стиль. Надеюсь это поможет Вам.
Дмитрий Думанский @doom369
карма
85,5
рейтинг 0,0
Co-Founder в Blynk
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (91)

  • –5
    oneByte размером с int это круто!
    • +2
      java.​io.​InputStream

      public abstract int read() throws IOException

      Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.


      java.lang.Byte

      MAX_VALUE A constant holding the maximum value a byte can have, 2^7-1
      MIN_VALUE A constant holding the minimum value a byte can have, -2^7.

      • –1
        Я знаю, в C getchar() тоже int возвращает дабы была возможность детектировать ошибку.
        Но всё равно выглядит очень странно такое название переменной и тип int.
        • +6
          int odin = 2; — как один тут товарищ писал, это странно, да :)
        • +1
          В С — как раз ничего странного. Где это возможно, используется тип int, т.к. он привязан к архитектуре процессора, под которую собран код — и обычно это максимально быстрая единица памяти, к которой имеет доступ процессор. Другими словами, экономия при объявлении переменной может обернуться либо тем же расходом памяти (в случае если компилятор их выровняет по границам, кратным размеру int), либо медленным доступом.
  • +4
    крошка сын к отцу пришел, и спросила кроха… :)
    • 0
      … Что такое singleton,
      И что такое mockup?

      :-)
  • +1
    > В случае явного указания значений для полей класса — они будут проинициализированы до вызова конструктора, что влечет дополнительные расходы.

    Какие расходы? На что?
    • +1
      Наверное, автор имеет в виду, что если у конструктора есть какие-то preconditions (например, аргументы не могут быть null), то даже если условия не выполнится, и швырнётся исключение, сначала всё равно будут проинициализированны поля.
      • +2
        Интересный вариант, но стаття как раз про то как не нужно делать. Поэтому preconditions в конструкторе не очень хороший вариант =).
        • 0
          Аргументируйте по поводу preconditions в конструкторе, пожалуйста. Пусть у нас есть класс, который в себе имеет final поля, которые не могут быть null. Как вы предлагаете это энфорсить? Отдельным методом validate, который разработчик, использующий класс, будет ещё должен не забыть вызывать? Или доверять пользователю класса в том, что он не передаст туда null, а если всё-таки передаст получать потом при вызове другого метода в совершенно другое время по неочевидной причине NullPointerException?
          • +1
            Если исходить из Ваших условий — приватный конструктор и статический метод в классе, который осуществит нужные проверки и вернет корректный обьект или ошибку.
            • +2
              «вернёт ошибку» — это в смысле кинет исключение? А какая тогда разница между ним и конструктором, кроме того, что он factory method?

              На самом деле мы довольно зря дискутируем, потому что со многими вашими утверждениями я согласен, просто хотел выразить мнение, что существует немало случаев, когда, например, проверка констрэйнтов в конструкторе — нормально.
          • 0
            Шаблон «фабрика» спешит на помощь.
            • 0
              Я знал, что обязательно кто-то это подметит. Шаблоны тем не менее не всегда уместны. На хабре была стаття по этому поводу.
              • +2
                Шаблоны уместны там, где они… уместны.
                И проверка всяких условий перед фактическим созданием объекта — как раз одно из мест, где применение шаблона «фабрика» просто напрашивается.
                • 0
                  Тоесть для этого примера я должен был бы создать шаблон «фабрика»?
                  • +2
                    Боюсь вам разочаровать, но шаблон «абстрактная фабрика» был создан и описан умными бородатыми дядьками в умной тонкой книжке почти двадцать лет назад…
              • +1
                Вы трижды одинаково опечатались в слове «статья». Это настораживает
                • +1
                  Я думаю на украинском. Извиняюсь, буду исправляться
            • 0
              Для value object-ов фабрику делать это страшный переинженринг. Однако у них вполне могут быть констрэйнты.
    • 0
      В случае если вы создаете обьект с пустым конструктором и инициализация полей Вам не нужна. А в пределах приложения это может быть существенно. В любом случае Вы правы, этот момент нужно подправить.
    • 0
      Например на

      private List zzz = new ArrayList();
      • 0
        А разница с тем, что он создастся в констукторе?
        • 0
          Например в конструкторе может быть дополнительная логика, которая будет менять/отменять инициализацию либо выполнять инициализацию полей более эффективным способом.

          Пример: конструктор принимает список элементов, эффективней будет сделать zzz = new ArrayList(providedValues), чем вначале просто проинициализировать список, а потом добавить в него элементы.
  • +4
    Object[] vs custom Class

    То же самое можно сказать и про кортежи: нередко встречал методы с сигнатурой типа
    public Triple<Long, Long, String> process(Map<Quadruple<Integer, String, String, String>, Pair<Double, Boolean>> map) 
  • +9
    Быть может повторюсь, но тем не менее.
    Есть еще полезный прием:

    "true".equals(str)

    Позволяет избежать NPE при сравнении строк с константами.
    • –7
      В принципе — да… Тем не менее 90% случаев сравнения строк — это
      label.equals(str)

      Этот случай больно уж редкий.
    • –2
      Не очень удачный пример, конкретно в данном случае лучше Boolean.valueOf(str);
      Но для других, нестандартных строк, действительно хороший вариант.
    • +3
      Так же очень удобно использовать null-safe

      Objects.equal(str, "true") из Guava

      или

      StringUtils.equals(str, "true") из commons lang
      • –2
        поддерживаю!
      • 0
        >Objects.equal(str, «true») из Guava

        В 7ке появился java.util.Objects. Не 1 в 1 с тем, что в Guava, но для equals/compare годится.
  • +7
    > Код с анонимными классами читается гораздо трудней и сложен для восприятия.

    Ну я бы поспорил, это очень субъективно, и еще зависит от конкретного кода. Часто такие решения достаточно универсальны и красивы.

    > String status = plan.getStatus();
    > if (status.equals(«draft»)) {

    Еще можно добавить, что это не только медленно, но и небезопасно, т.к. plan.getStatus() теоретически может вернуть null, и тогда огребем NPE при вызове equals().
    Лучше сравнивать строки так:

    «draft».equals(status)
    • 0
      >>Ну я бы поспорил
      Я ведь написал — в некоторых ситуациях очень применимы.
      Здесь предполагалось что часть модели Plan.status не может быть null. В виду constraint БД.
      • 0
        а почему не переписать ето все на switch? он явно быстрее последовательности if-ов.
        • 0
          Потому что это не 7-я Java. Старые проекты врятли уже когда-нибудь перейдут на 7-ю.
          • +1
            Ну тогда хотябы в варианте с enum, они то с 1.5 уже были доступны.
            • 0
              В проекте так и сделано, это просто для примера.
  • +1
    Вот тут отличная подборка по подобным советам: www.odi.ch/prog/design/newbies.php

    К слову. Автор кажется туда заглядывал.
    • +3
      Есть даже целые сайты javaantipatterns.wordpress.com/. Надо гуглить по запросу «java anti-patterns».
      • +1
        > Надо гуглить по запросу «java anti-patterns».

        Категорически плюсую, очень интересные и неочевидные вещи попадаются. Еще добавлю в копилку слов для гугления полезных советов — «java exceptions anti-patterns».
  • +1
    По поводу name.isEmpty() — это появилось только в JDK 1.6. Безопасней будет либо сравнить с константой "", либо по аналогии name.lenght() == 0. Хотя второй вариант встречался реже
    • +6
      А так как в 90% случаев надо среагировать и на null, и на "", то еще лучше использовать org.apache.commons.lang.StringUtils.isEmpty( s )
      • 0
        Да, полностью с вами согласен. Обычно используем либо StringUtils.isEmpty либо StringUtils.isBlank в зависимости от ситуации. Я привел пример без использования внешних библиотек.
  • 0
    Насчёт ArrayList vs HashSet. Ведь маппинг поддерживает только set? Вот маленький кусочек из маппинга класса проекта, над которым в данный момент работаю:

    Лучше приводить каждый раз к листу/массиву?
    • 0
      Совсем забыл про то, что маппинг — xml -_-
      <set name=«comments» inverse=«true» order-by=«id»>
        <key>
          <column name=«agentID» />
        </key>
        <one-to-many class=«data.Comment» />
      </set>
  • 0
    >>Object[] vs custom Class
    О боги, неужели вариант «плохо» ещё кто-то пишет? Это вообще диковинка какая-то)
    За цикл статей — спасибо, уже пару полезных для себя мелочей заметил!
    • 0
      Да, я и сам иногда грешу =), но только когда очень спешу и проще и быстрей написать так. Когда например делается выборка 2-х колонок из разных таблиц.
      • +3
        Даже если спешить, в любой IDE класс с public полями сделать не намного медленнее, чем Object[]. А читать на порядок проще.
    • –2
      Наверно, это последствия детской травмы, полученной программистом при изучении конструкторов С++
    • +1
      Я пишу. Плачу, но пишу. Потому что в среде j2me приходится экономить память, а создание классов под то, что можно реализовать массивами — непозволительная роскошь.
      Вернее, позволить-то можно себе. Но тогда проблемы вылезут в другом месте.
  • –4
    С первым пунктом я не согласен. Есть какой-то бенчмарк в подтверждение?
    • 0
      public static void main(String args[]) throws IOException {
      		//~5mb
      		File file = new File("D:\\P1010387.jpg");
      			
      		readFile(new BufferedInputStream(new FileInputStream(file)), "Buffered");
      		readFile(new FileInputStream(file), "Not buffered");
      	}
      	
      	private static void readFile(InputStream is, String type) throws IOException {
      		System.out.println(type);
      		System.out.println("Start : " + new Date());
      		int val = 0;
      		while ((val = is.read()) != -1) {
      			val = val + 1;
      		}
      		System.out.println("End : " + new Date());
      	}
      
      • +3
        Ужас. new Date()?

        Есть же System.currentTimeMillis();
        • –5
          Время — деньги
    • +2
      Хм, теоретически, ОС и сама может закешировать файл, и чтение всё-равно будет из памяти, но это будет зависит от конкретной джавамашины, ОС, настроек и фазы луны.
      • 0
        Я поэтому и усомнился. ОС сама кеширует чтение из диска. Мне показалось сомнительным пытаться оптимизировать такие вещи на уровне Java.
      • +2
        Вот только на каждое чтение может происходить переключение контекста, поиск и проверка на допустимость хэнделера файла, поиск в кэше, вызов драйверов и т.п. Кроме того, ОС не знает будет ли произвольный или последовательный доступ к файлу и потому упреждающее чтение будет использоваться очень осторожно. Кроме того упреждающее чтение как правило работает только для нефрагметированных данных (гонять головки диска, чтобы прочитать данные без уверенности, что они могут понадобиться — дорогое удовольствие). В общем можно считать, что упреждающего чтения в ОС нет.
        • 0
          Для меня полное откровение, уверен был, что на упреждающее чтение можно смело наедятся. Где можно про это поподробнее узнать?
  • +1
    isEmpty() касается и коллекций, поскольку далеко не все они хранят информацию о своем размере, а это ведет к необходимости полного перебора.
    • 0
      А какие — не хранят?
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Size дорого, но isEmpty уже не так дорого.
          • 0
            Для isEmpty достаточно проверить, есть ли хоть один элемент. Это практически в любом случае делается за фиксированное время.
  • –1
    Что касается файлового IO, правильно использовать java.nio.*, а не вникать в неудачные места устаревшего API.
    • +1
      Очень зависит от окружения. Советую просмотреть вот эту штуку, а также вспомнить, какой шаг вперёд сделал Linux в многопоточности от 2.4 к 2.6.
      • +1
        Бонус с интересными комментариями.
  • +3
    Правда, в этом случае есть один большой минус — стоимость поддержки приложения увеличивается, особенно это становится заметным, когда нужно добавить, удалить или изменить одно из существующих состояний.

    Перечисления же модифицировать, наоборот, проще. Изменил в объявлении, и оно поменялось везде. А вот со строками придётся вручную искать все упоминания в коде и разбираться, нужно здесь менять, или это просто совпадение имён.
  • 0
    В дополнение — постоянно при добавлении вычисляется хеш, который для сложных обьектов может стоить дороговато.

    По умолчанию джава использует в качестве хеша что-то вроде адреса объекта в памяти, поэтому обычно генерироваться он будет быстро.
    • +1
      Для сложных объектов скорее всего будет сложный хеш. Причем, он еще в подавляющем большинстве случаев еще и не кэшируется и будет честно считаться каждый раз при работе с хешсетом.
      • 0
        +1-1
        Сложный хэш будет. Но считаться по канонам Java он должен лишь раз. В противном случае вы свой объект в том же Set'е никогда не найдёте.
        • –1
          Это не так. Попробуйте добавить объект в HashSet 2 раза и проверьте, сколько раз будет вызван метод hashCode. Ответ — 2 раза. Более того, если после добавления элемента в HashSet вы поменяете его состояние и этим измените hashCode, то получите шикарный баг. Итерируясь по коллекции вы сможете найти объект, но тем не менее contains вернет false (как раз таки объект вы не действительно найдете в HashSet) и добавить его можно будет еще раз, уже в другое место массива. Я собственно это в свое время и словил, ибо не понимал что HashMultimap слово Hash от HashSet, а не как я думал от HashMap. ArrayMultimap решил ту проблему.

          HashSet[Object] q = new HashSet[Object]();
          Object o = new Object() {
          @Override
          public int hashCode() {
          System.err.println(«hashCode»);
          return super.hashCode();
          }
          };
          q.add(o);
          q.add(o);

          Тем не менее, кэшировать хэшкод это очень правильная идея в плане перформанса, но поддержка этого кэширования в коде слишком трудоемко для сложных случаев, где от кэша собственно и будет эффект, ибо кэш надо будет сбрасывать на любую мутацию объекта, в итоге не встречал, чтобы ктото хэш кэшировал на практике.
          • +1
            > в итоге не встречал, чтобы ктото хэш кэшировал на практике.
            Все встречали.
            java.lang.String. :)
            • –1
              Immutable это нечестно, такое встречал конечно и не раз. :)
          • 0
            Во-первых, вам стоит научится пользоваться дебаггером, это избавит от необходимости каждый раз вставлять System.out и перекомпилироваться.

            Во-вторых, читать документацию. Стоит изучить так называемый «Общий контракт работы с хэш кодами» (The general contract of hashCode) из JDK.

            Стоит также почитать всякие заметки, чтобы понять, что это делается не только для перформанса.

            И в конце концов, читайте то, что вам собеседники пишут. Я описал то же самое, ожидая, что коллеги по цеху меня поймут.
            >>В противном случае вы свой объект в том же Set'е никогда не найдёте.
            >тем не менее contains вернет false

            Ко всему прочему, изучите имплементацию HashSet, она построена не на массиве, и в комментариях тут это уже даже упоминалось.

            Если не умеете перегружать хэш коды самостоятельно, воспользуйтесь Apache HashCodeUtil, построенной, как помню, на простых числах.
            • 0
              1. По контракту хешкод обязан сохранять свое значение при условии, что информация используемая в equals не была обновлена. С-но, если же эта информация была обновлена, то хэш код изменится, и это не нарушает контракт хэш кода. Посмотрите javadoc к методу hashCode.

              method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.

              2. HashSet построен на основе HashMap, который в свою очередь построен на основе массива.
              /**
              * The table, resized as necessary. Length MUST Always be a power of two.
              */
              transient Entry[] table;

              3. HashCodeUtil или любой другой аналог, не поможет вам кэшировать хэш в мутабл объекте. Потому что этот кэш придется сбрасывать на любое изменение объекта. И вообще рекомендовать Apache Commons это неразумно, он застрял на уровне семантики явы 1.4 и его развитие неудовлетворительное. Есть отличный форк от него под названием Guava, в которой есть аналогичные метода, но уже с семантикой 6й явы, например, Objects.hashCode(...).

              4. Вы написали, что хэш будет считаться по канонам явы один раз на объект. Это очевидно неправда.

              HashMap:
              public V put(K key, V value) {
              if (key == null)
              return putForNullKey(value);
              int hash = hash(key.hashCode());
              В самом начале метода put у HashMap у ключа тупо вызывается hashCode, на каждый put/add/contains итп будет вызван этот метод.

  • 0
    У нас на проекте принято использовать для логгера
    private Logger logger = Logger.getLogger(getClass());
    опять же, несмотря на некоторую неэффективность данного подхода, зато очень удобно копипастить и нет глупых ошибок при этом.

    И я бы наоборот советовал, везде где это возможно, инициализировать поля, там же где они объявлены. Код все еще чистый, и на одну строчку короче. Для того и предзначена эта возможность, чтобы не засорять этим конструктор.
    • 0
      Про сериализацию. Я бы не советовал, без крайней нужды использовать стандартную ява сериализацию. Protobuf как правило намного более удобное решение. В принципе, вообще сильная связность объекта с его сериализацией, его компаратором или с его реализацией методов hashCode/equals как правило это плохая идея. Впрочем, по последнему пункту красивых решения я не встречал, поэтому все упирается во wrapper-декоратор объекты, которые просто перегружают методы hashCode/equals.
      На самом деле это все большая проблема, потому что написать адекватный контейнер класс, который содержит 2 банальных стринг поля и по сути обычный pojo bean занимает у меня уже до 3х экранов кода, что конечно ужас. И, что печально, код везде повторяется, а вот без рефлекшена его вынести куда-то не выходит, а рефлекшен слабое решение с позиции перформанса, а вот для таких контейнеров как раз перформанс важен, ибо они обычно пролетают всю систему и несколько потоков меняют при этом.
    • 0
      Толковая IDE может сама обновлять имя класса при копировании такой строчки.
      • 0
        Intellij Idea/Eclipse не умеют.
        • 0
          Как раз-таки Idea умеет. Насчет Eclipse не знаю.
          • 0
            И как этим пользоваться? Если просто скопировать, то 10.5.2 Idea не правит название класса. Тут есть какой-то хинт?
  • +1
    По теме данной статьи можно почитать очень хорошую книгу Effective Java, автор Joshua Bloch. Там целая книга примеров правельного написания кода на Java.

    Даже сам
    • +2
      Даже сам James Gosling (fellow and vice president, Sun Microsystems, Inc., and inventor of the Java programming language) писал об этой книге:
      I sure wish I had this book ten years ago. Some might think that I don’t need any Java books, but I need this one.
  • 0
    Почему еще никто не сказал про интернацию.
    • –1
      Тут очень интересный ответ, поэтому не добавлял.
      • 0
        Деградация после 25к элементов — не новость.
        Если Вам нужно запихнуть столько строк в пул — где-то что-то делаете не так. Для констант и сравнения интернация весьма полезна.
  • –1
    если бы один раз, но целых 3 раза, поэтому:

    //плохо
    стаття
    //хорошо
    статья
  • 0
    www.odi.ch/prog/design/newbies.php — в копилку. анти-паттерны :)
  • +1
    >И еще — считывайте данные порциями, а не по байтам, это тоже позволит прилично сэкономить.

    Считывание данных порциями (java.io.InputStream.read(byte b[], int off, int len)) реализовано как вызов побайтного считывания в цикле (java.io.InputStream.read()). :) 1.6.0_29

    Так что морочиться этим не обязательно.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.