0,0
рейтинг
9 ноября 2011 в 19:45

Разработка → Маленькие хитрости Java из песочницы

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

new vs valueOf

//медленно
Integer i = new Integer(100);
Long l = new Long(100);
String s = new String("A");

//быстро
Integer i = Integer.valueOf(100);
Long l = 100L;//это тоже самое что Long.valueOf(100L);
String s = "A";


Старайтесь всегда использовать метод valueOf вместо конструктора в стандартных классах оболочках примитивных типов, кроме случаев, когда вам нужно конкретно выделить память под новое значение. Это связано с тем, что все они, кроме чисел с плавающей точкой, от Byte до Long имеют кеш. По умолчанию этот кеш содержит значения от -128 до 127. Следовательно, если ваше значение попадает в этот диапазон, то значение вернется из кеша. Значение из кеша достается в 3.5 раза быстрее чем при использовании конструктора + экономия памяти. Помимо этого, наиболее часто используемые значения могут также быть закэшированы компилятором и виртуальной машиной. В случае, если ваше приложение очень часто использует целые типы, можно увеличить кеш для Integer через системное свойство «java.lang.Integer.IntegerCache.high», а так же через параметр виртуальной машины -XX:AutoBoxCacheMax=<size>.

+ vs append

//медленно
String[] fields = new String[] {"a","b","c","d","e","f","g"};
String s = "";
for (int i = 0; i < fields.length; i++) {
	s = s + fields[i];
}
return s;

//быстро
String[] fields = new String[] {"a","b","c","d","e","f","g"};
StringBuilder s = new StringBuilder();
for (int i = 0; i < fields.length; i++) {
	s.append(fields[i]);
}
return s.toString();


Никогда не используйте операции конкатенации (оператор +) строки в цикле, особенно если таких операций у вас много, это может очень существенно снизить производительность. Все это происходит потому, что в приведенном выше примере «s = s + fileds[i]» выполняется целых 3 операции: создается StringBuilder на основе строки s, вызывается метод конкатенации append, после конкатенации вызывается метод toString (выглядит так: s = new StringBuilder(s).append(fields[i]).toString();). Целых 3 операции вместо одной! Помимо этого каждый результат s + fileds[i] будет занимать память в куче, как отдельная строка.

StringBuilder vs StringBuffer

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

instanceOf

Старайтесь как можно реже использовать оператор instanceOf. Это один из самых медленных java операторов и подходить к его использованию нужно осторожно. Имейте в виду — чаще всего наличие этого оператора в коде означает непонимание принципов ООП, нежели попытка реализовать некий паттерн. Почти всегда полиморфизм способен помочь избавится от этого оператора.
P. S. Многие в комментариях аппелируют к «Это один из самых медленных java операторов». Это действительно так. Конечно, не совсем корректно сравнивать операторы языка по производительности, так как они выполняют абсолютно разные задачи, но, тем не менее, механизм работы instanceOf гораздо сложнее, например, оператора '*'.

Generics

//плохо
List a = new ArrayList();

//хорошо
List<String> a = new ArrayList<String>();


Всегда старайтесь типизировать ваши коллекции, методы и классы. Это избавляет сразу от 2-х потенциальных проблем: приведение типов и ошибок выполнения. Также назначение таких коллекций легче воспринимать. Особенно часто этим пренебрегают мои американо-индусские коллеги. Если же ваша коллекция должна содержать обьекты разных типов — используйте <?>, а еще лучше <? extends someType> тогда зная общий класс/интерфейс для всех обьектов вам не прийдется делать приведение типов и применять оператор instanceOf.

Interface for Consts

//плохо
interface A {
	public static final String A = "a";
}

//хорошо
final class A {
	public static final String A = "a";
}

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

Override

Старайтесь использовать Override аннотацию для методов, которые переопределяют методы супер классов. Это позволяет сразу избежать опечаток и повышает читаемость кода (позволяет сразу понять, что у супер класса есть такой же метод не открывая родительский класс).

null vs empty

Всегда старайтесь в методах вашей бизнес логики возвращать пустые коллекции вместо null значений, это избавляет от лишних null-проверок и делает код чище. Для этого в классе Collections есть несколько замечательных методов:
Collections.emptyList();
Collections.emptyMap();
Collections.emptySet();

В комментариях просят уточнить вариант применения. Типичный случай:

Set<Document> docs = getDocuments(plan);
for (Document doc : docs) {
	sendMessage(doc.getOwner());
}
public static Set<Document> getDocuments(Plan plan) {
	//some logic
	if (isDoSomethingOk() && hasAccessToDocuments(user)) {
		return plan.getDocuments();
	}
	return Collections.emptySet();
}

Конечно, это не значит что эта техника должна быть применена абсолютно везде. Скажем, если Вы сериализируете обьекты, то тут лучше подойдут null значения. Собственно — «в методах вашей бизнес логики» как раз и означает применение в логике, а не в модели.

Преобразование чисел

//медленно
int a = 12;
String s = a + "";
//быстро
int a = 12;
String s = String.valueOf(a);

Ситуация очень схожа с конкатенацией строк.

Выводы

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

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

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

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

  • +4
    Старайтесь как можно реже использовать оператор instanceOf. Это один из самых медленных java операторов

    А можно поподробнее? Что значит «самый медленный оператор»?
    • +5
      «instanceof» на интерфейсах использует достаточно сложную систему проверок из-за возможности реализовывать множество интерфейсов (а также из-за их собственного дерева наследования). Собственно, этого вполне достаточно, чтобы этот оператор можно было считать медленным. Хотя, на классах эта проверка делается за константное время.
      • –1
        Все верно, в то же время, если противопоставлять instanceOf и полиморфизм, то разницы в скорости практически не будет, так как используются одни и те же механизмы.
        • +10
          Для виртуального вызова используется таблица виртуальных функций и, зачастую, спекулятивная оптимизация. Это пара обращений к памяти.

          Определение принадлежности к интерфейсу — практически поиск в глубину по дререву предков класса.
      • –3
        Блин, xappymah, не от тебя я хотел ответ услышать :)
    • +6
      • –2
        В общем случае (при применении к интерфейсам) — медленный, т.к. против оптимизации поиска интерфейсов в дереве наследования в HS несложно создать контр-пример, который сведет ее на нет.
        Впрочем, в реальной жизни такие контр-примеры случаются реже.
        • НЛО прилетело и опубликовало эту надпись здесь
  • +9
    Оберните код тегом «source».

    Все внятно и очевдно. Читается в Effective Java. Вот на счет интерфейса для констант не соглашусь. Часто встречаю такую практику и считаю ее примлемой. Хотя, сам стараюсь писать финальный класс.

    Пруфф по поводу интерфейсов для констант.
    • +2
      нет ничего плохого определить константы в интерфейсе, если он не реализуется ни одним классом, но, поскольку гарантировать этого нельзя, то лучше не надо.
    • +3
      Всё-таки думаю, что это не «пруфф». К сожалению, Java API не везде спроектировано идеально (особенно, ранние версии). Есть куча примеров плохой реализации и проектирования API в Java (например, тут нелпохое обсуждение: stackoverflow.com/questions/186964/java-core-api-anti-patterns-what-is-wrong). Тот же Блох, на книгу которого вы ссылаетесь, приводит много примеров плохого JDK API.
    • +2
      Спасибо за тег. Такая практика действительно частая, собственно это и подвигло меня к этому опусу.
    • +2
      В той же Effective Java, если мне не изменяет память, говорится и об интерфейсах для констант: люди чтобы получить shortcut к определённым в интерефейсе константам говорят, что клас его реализует, и получается полный трешняк с точки зрения ООП.
      • –1
        Для получения shortcut'ов на константы можно использовать static import. Но и его нужно использовать очень осмотрительно.
  • +2
    Неужели всё это не оптимизируется современными компиляторами?
    Реквестирую пруфы с замерами производительности!
    • +6
      строковую конкатенацию вроде оптимизируют в новых JDK. А с new vs valueOf оптимизация затруднительна — new должен гарантированно давать новый объект (мало ли кто что потом будет с ним делать, например использовать в качестве монитора синхронизации или ключа в IdentityHashMap)
      • –1
        Да, оптимизация происходит, но не в случае что я описал. Компилятор оптимизирует код, но в своем понимании:
        s = s + fileds[i]
        транслируется в
        s = new StringBuilder(s).append(fields[i]).toString();
        • –1
          Но ведь логика-то какая-то должна в этом быть. Почему именно так, а, например, не как вручную?
          • –1
            Ну, я имею в виду в контексте приведенного примера. Почему бы не использовать один StringBuilder для массового сложения строк? Неужели подобный анализ невозможно провести?
            • 0
              String[] fields = new String[] {"a","b","c","d","e","f","g"};
              String s = "";
              for (int i = 0; i < fields.length; i++) {
                  s = s + fields[i];
                  doSomeThing(s);
              }
              


              на момент вызова doSomeThing(s) s должно быть вычислено
              • 0
                Если я правильно понял, тут отличие лишь в том, что при каждой итерации будет создаваться новый StringBuilder?
                • 0
                  это я пытаюсь показать почему нельзя прооптимизировать как вы хотите, путем избежания создания StringBuilder.
        • +1
          там есть хитрая опция XX:+OptimizeStringConcat, которая, по идее, должна убирать StringBuilder.toString() в ситуациях когда можно обойтись без него. Но документация по ней туманна, когда она реально помогает мне не понятно
  • +3
    Еще часто приходится видеть такое «экспресс»-преобразование чисел в строки
    int a = 12;
    String s = a + "";
    
    вместо
    String s = String.valueOf(12);
    • +2
      На пхп любят писать еще эпичнее:
      $a = "2";
      $a = $a + 0; // преобразовуем в число
      • 0
        Это дичайший реликтовый образец. :)

        Ну я еще могу понять, когда появляются странные штуки при реализации логики, взаимодействия классов. Но вот такой хитровыкрученный пример — это явный «изыск».
        • 0
          Да, ещё в JavaScript можно сделать быстрое приведение:
          var a = '7';
          var b = a - 0;
          


          А в PHP достаточно такого: $a = (int|bool|array|string|object)$a;
          • 0
            А в PHP достаточно такого: $a = (int|bool|array|string|object)$a;


            Ну в этом-то и дело.
            Сейчас ради интереса посмотрел в мануал по PHP: найти описание явного приведения типов намного проще, и легче запомнить, чем немаленькие таблицы неявных приведений.

            Отсюда вопрос: кто будет сознательно использовать приведение типов через арифметические (строковые) операции?

            Ну и в той же Java: ну кому, кому блин может придти в голову, намеренно преобразовать число в строку через конкатенацию числа и строки? :)
            • 0
              ну кому, кому блин может придти в голову, намеренно преобразовать число в строку через конкатенацию числа и строки
              Кажется, что в голову прийти не может, пока не начинаешь читать чужой код и общаться на форумах) Такое (и хуже) сплошь и рядом. Ниже каменты с плюсами, где на это говорится в духе «а что такого».
      • 0
        Все-таки я нашел этот коммент )
    • +2
      Спасибо, действительно такое часто происходит. Добавил в топик
    • 0
      Может быть автор имел в виду
      String s = String.valueOf(a);
      поскольку не проще ли
      String s = «12»;
      • 0
        Да, именно так
  • +3
    А еще маленькая хитрость (на правах иронии):
    вместо
    for(i=0;i<array.length;i++) item=array[i];

    используйте
    for(item:array)
    • –8
      Вообще, никогда не используйте .length внутри цикла, т.к. тогда он будет на каждой итерации вызывать метод подсчета длины
      • +3
        1. неужто нет оптимизации кешированием?
        2. какой пересчет? длина массивов неизменна
        • –6
          Тем не менее — метод будет вызываться каждый раз
          • +1
            .length — не метод, а свойство.
            Но вполне допускаю, что для коллекций size() будет вызываться на каждой итерации
            • –4
              А не геттер?
              • +4
                позвольте, это Java, а не .NET — здесь с этим немного очевиднее
                • –2
                  Ах, точно. В последнее время одновременно пишу минимум на двух-трех языках. По-этому иногда путаюсь :)
                  • 0
                    Особенно опасно писать на внешне похожих языках\платформах… Начинаешь необоснованно на одной платформе бояться\пытаться использовать особенности\баги другой платформы, даже если первой это не свойственно — а это о ч е н ь чревато.
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                  Кстати, да. В случае коллекций — вспомнил — тоже. Потому что, если размер изменится, причем не с помощью iterator.remove(), то получим ConcurrentModificationException.
                  • 0
                    Только получим его не на проверке размера, а на обращении к «просроченному» итератору в случае foreach-нотации.
                    В случае обхода List'а через size() exception'а не будет в случае обращения по индексу (в случае отсутствия конкурентного изменения несколькими потоками), однако рискуем пропустить несколько элементов в случае удаления элементов из коллекции без изменения переменной цикла. Иногда замечается у студентов-нубов, переползающих с C/C++.
            • 0
              arraylength — это инструкция

              Во втором случае она будет вынесена за цикл, поэтому такой хитрожопый код будет работать:
              private static void two(String[] fields) {
              	for (String s : fields) {
              		System.out.println(s);
              		fields = null;
              	}
              	System.out.println(fields);
              }
              


              а такой отвалится с NPE:
              private static void one(String[] fields) {
              	for (int i = 0; i < fields.length; i++) {
              		System.out.println(fields[i]);
              		fields = null;
              	}
              	System.out.println(fields);
              }
              
              • 0
                Нет, не поэтому. А потому, что в первом случае используется fields.iterator(), с которым ничего не происходит, когда обнуляем сам fields, а во втором на каждой итерации пытаемся обращаться к fields, который успешно зануллен в конце первой итерации
                • +4
                  Давайте будем проверять :) Никакого итератора для массивов нет.

                  private void one(java.lang.String[]);
                    Code:
                     0:	iconst_0
                     1:	istore_2
                     2:	iload_2
                     3:	aload_1
                     4:	arraylength
                     5:	if_icmpge	23
                     8:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream;
                     11:	aload_1
                     12:	iload_2
                     13:	aaload
                     14:	invokevirtual	#3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
                     17:	iinc	2, 1
                     20:	goto	2
                     23:	return
                  
                  private void two(java.lang.String[]);
                    Code:
                     0:	aload_1
                     1:	astore_2
                     2:	aload_2
                     3:	arraylength
                     4:	istore_3
                     5:	iconst_0
                     6:	istore	4
                     8:	iload	4
                     10:	iload_3
                     11:	if_icmpge	36
                     14:	aload_2
                     15:	iload	4
                     17:	aaload
                     18:	astore	5
                     20:	getstatic	#2; //Field java/lang/System.out:Ljava/io/PrintStream;
                     23:	aload	5
                     25:	invokevirtual	#3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
                     28:	aconst_null
                     29:	astore_1
                     30:	iinc	4, 1
                     33:	goto	8
                     36:	return
                  
                  }
                  
                  
                  • –3
                    Мы можем массив засунуть в for(:) только потому, что он является внутренним обьектом Array, который реализует Iterable, а в нем определен iterator().
                    • +3
                      нет, foreach работает по разному для массивов и Iterable и массив не реализует Iterable
                    • +3
                      Зачем писать если не знаете? Проверили бы.
                    • 0
                      Массив реализует ровно два интерфейса — Cloneable и Serializable.
                      Об этом явно написано в JLS (который Вы, скорее всего, еще не открывали :-) ).
                  • +2
                    Прошу прощения, правильный листинг такой, но суть не меняется:

                    private static void one(java.lang.String[]);
                    Code:
                    0: iconst_0
                    1: istore_1
                    2: iload_1
                    3: aload_0
                    4: arraylength
                    5: if_icmpge 25
                    8: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream;
                    11: aload_0
                    12: iload_1
                    13: aaload
                    14: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
                    17: aconst_null
                    18: astore_0
                    19: iinc 1, 1
                    22: goto 2
                    25: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream;
                    28: aload_0
                    29: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
                    32: return

                    private static void two(java.lang.String[]);
                    Code:
                    0: aload_0
                    1: astore_1
                    2: aload_1
                    3: arraylength
                    4: istore_2
                    5: iconst_0
                    6: istore_3
                    7: iload_3
                    8: iload_2
                    9: if_icmpge 33
                    12: aload_1
                    13: iload_3
                    14: aaload
                    15: astore 4
                    17: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream;
                    20: aload 4
                    22: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
                    25: aconst_null
                    26: astore_0
                    27: iinc 3, 1
                    30: goto 7
                    33: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream;
                    36: aload_0
                    37: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
                    40: return

                    }
                    • +1
                      но предыдущий был красивее оформлен = (
              • 0
                В первом случае неявно создаётся дополнительная ссылка на массив?
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          А разве в простых случаях такой стандартный fori не съеживается до ассемблерного лупа?
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              «N итераций» это настолько частый случай в программировании, что можно смело полагаться на jit компилятор в том, что он оптимизирует его настолько, насколько возможно. В идеале сведет к loop. Поэтому не надо думать за компилятор.

              Так я рассуждаю в подобных случаях.
              • НЛО прилетело и опубликовало эту надпись здесь
                • 0
                      mov cx, 10
                  label:
                      ...
                      loop label
                  
                  • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Эх. Современные кодогенераторы практически не используют инструкцию «loop». «Практически» — написано, потому что я не могу на 100% сказать про все существующие в природе кодогенераторы. ;)
                    С ней куча ненужного гемора по сравнению с обычными cmp & jmp. Начиная с помех регаллокатору, и заканчивая вложенными циклами.
                    С появлением макофьюжена — вообще стала бессмысленна.
                    Живет только потому, что Intel не выбрасывает ничего ненужного. ;)
                    • 0
                      Макрофьюжена.
                    • 0
                      Ну так поэтому я и не уточнил в первом комментарии, имея ввиду любой из простых ассемблерных циклов, а не перекладывание значений с места на место, которым якобы должна заниматься виртуальная машина в лице процессора.
                      • 0
                        *заниматься при виде
                        for (int i = 0; i < array.length; i++)
      • –1
        Можно еще так писать:

        for(int i=0, size = array.length; i < size; i++) item=array[i];

        * This source code was highlighted with Source Code Highlighter.
        • –3
          Лучше использовать итератор (или for() c Java 1.5, что то-же самое) ибо у array[i] появляются накладные расходы на получение значения по индексу.

          У итератора время получения значения — постоянное и почти не зависит от размера коллекции.
          «почти» — все зависит от того, насколько равномерно распределены элементы и отсутствие коллизий.
          • +2
            Ой. Как все запущено…
          • 0
            все зависит от того, насколько равномерно распределены элементы и отсутствие коллизий


            <irony> Ага, для массивов все именно так и происходит. :-) Прощай, скорость. :-) </irony>
        • +1
          Сюрпрайз. Для
          for(int i=0;i<array.length;i++)

          и
          for(int i=0, size = array.length;i<size;i++)

          Будет порожден один и тот же x86 код при использовании ключика -server.
          Про -client просто не знаю. Но кому нужен -client?
          Мой ноут по дефолту определяется как server-side machine. :)

          ЗЫ Да я знаю случаи, когда вышесказанное не так. Но обычный и правильный девелопер с таким вряд ли столкнется. ;)
    • 0
      вместо
      for(i=0;i<array.length;i++) item=array[i];

      используйте
      for(item:array)


      Существует мнение, почёрпнутое из собеседования одного очень мозговитого товарища, долгое время занимавшегося декомпилятором и анскремблером Java, что foreach чрезвычайно невыгодная операция, и что для коллекций она компилируется в fori с предварительной конвертацией коллекции в Array. Отвечать за правдивость и распространённость такого поведения не хотелось бы, но умолчать об этом тоже не в силах.
      • 0
        Т.е. звучит так: «никогда не используте!»?
      • +2
        > с предварительной конвертацией коллекции в Array
        это неправда.
        • 0
          Слава богу!
          Я напишу ребятам из своей прошлой конторы, пускай дадут челу внеочередной отпуск.
  • +4
    А вообще очень много таких советов может дать какой-нибудь FindBugs или Checkstyle
  • +1
    А есть разница в скорости между
    String[] fields = new String[] {"a","b","c","d","e","f","g"};
    String s = "";
    for (int i = 0; i < fields.length; i++) {
        s = s + fields[i];
    }
    return s;
    и
    String[] fields = new String[] {"a","b","c","d","e","f","g"};
    String s = "";
    for (int i = 0; i < fields.length; i++) {
        s += fields[i];
    }
    return s;
    • +7
      Никакой.
    • +2
      В обоих случаях конкатенация будет сведена в один и тот же байткод, использующий StringBuilder, так что разницы быть не должно.
    • +2
      Более того, в Java даже нет разницы между this.intfield++ и this.intfield = this.intfield + 1
      • +3
        И в C# тоже. В свое время это поразило меня до глубины души, когда я (ну молодой был) полдня пытался понять, почему моя многопоточная программа не работает, и даже подумать не мог на ++, т.к. считал ее заведомо атомарной. Потом уж узнал про чудо-метод Interlocked.Increment…
  • +4
    Кстати современные IDE большинство этих случаем пометят как «недоработку», пользуюсь эклипс и она очень хорошо эти косяки подмечает, а в итоге и сам юыстро привыкаешь так не делать.
  • 0
    А какой смысл в современных промышленных Java-приложениях, вовсю использующих мегабайты спрингов, хайбернейтов и прочей дживити, заниматься оптимизацией на уровне инициализации строк? То есть с точки зрения чистоты кода и дзена программирования оно понятно, что надо делать как надо, но вы же апеллируете именно к скорости выполнения. Очень часто проблем с ней и с нагрузкой на сервер не возникает, а если возникает, бизнес-логику смотреть надо в первую очередь, потом фреймворки, а не эти мелочи.
    • +3
      Проще ввести везде положительную практику, чем потом втыкать в профайлер. Кроме того использование объекта строителя несколько яснее выражает идею кода, ИМХО.
      • 0
        Если у вас есть проблемы с производительностью на уровне криво написанной бизнес-логики, отсутствием кэширования, раздутым стеком фреймворков, то применение всех вышеописанных методик поможет увеличить производительность вашего кода не более, чем на 0,19% (в лучшем случае и только для небольшого проекта).
        • 0
          Если такая печаль то практики всё-равно не соблюдаются и баланс света с тьмой восстанавливается сам по себе.
        • 0
          Мало того, если во всех случаях использования строк расово верно всё делать через StringBuilder, не задумываясь о потерянном на лишнее кодирование времени разработчика, и полученных от этого сотых долях процента прироста производительности, то к концу срока разработки проекта может оказаться, что суммарного количества потерянного времени могло бы запросто хватить на пару-тройку серьёзных оптимизаций бизнес-логики и прироста производительности процентов на 200 8)
        • 0
          Насчет сложения строк в цикле.

          Если это какой-нибудь legacy-код разбора XML, то в результате сбора строчки размером в 2 Mb можно потерять несколько драгоценных секунд. :-)
    • +1
      Далеко не все java проекты проекты используют стэк из spring, hibernate и им подобных и далеко не все java проекты промышленные. Чаще всего это аутсорс и большие компании. А в маленьких игровых проектах и специализированных приложениях вполне обходятся без всего этого груза.
      • 0
        С маленьким джава-игроделом не сталкивался, но предположу, что и там свои библиотеки есть, и графика ресурсы жрет, и в случае необходимости нужно будет таки запускать профайлер, и толку от него будет больше на полтора-два порядка.
    • 0
      >А какой смысл в современных промышленных Java-приложениях, вовсю использующих мегабайты спрингов, хайбернейтов и прочей дживити, заниматься оптимизацией на уровне инициализации строк?

      Вы, вероятно, забыли, что Java — это не только «современные промышленные Java-приложения, вовсю использующие мегабайты спрингов, хайбернейтов и прочей дживити», но и, скажем, мобильные приложения (Android).
      • 0
        Я это прекрасно понимаю, но тем не менее не думаю, что и на Андроиде можно выиграть в производительности, просто избавившись от instanceof и конструкторов строк. Да, так надо делать, да так лучше, но к производительности это имхо не имеет практически никакого отношения.
        • 0
          но к производительности это имхо не имеет практически никакого отношения.

          Ну так и топик не называется «повышение проивзодительности java приложений».
          • +1
            кеш...3.5 раза быстрее… экономия памяти...IntegerCache.high...//медленно… очень существенно снизить производительность… Целых 3 операции вместо одной!.. производительность будет выше… один из самых медленных java операторов… — это ведь из статьи все.

            Ну и мажорная нота в финале: «не плохо ускорить работу ваших программ, сами того не подозревая».
            • +1
              Доводилось тестировать банковское ПО, написанное программистами с вашим подходом. В системных требованиях сотни мегабайт ОЗУ и прочее. Под неполной нагрузкой ведет себя черте как. В чем проблема написать хотя бы для конкатенации строк в цикле более быстрый вариант вместо «ленивого» короткого? Не будут плодиться лишние сущности и задирать планку требований. На хабре кстати была статья именно про оптимизацию работы со строками на примере пром приложения.
              • +1
                То есть вы хотите сказать, что после оптимизации конкатенации строк приложение взлетело? Перечитайте пожалуйста то, что я писал выше. Это не мой подход, я применяю практически все описанное здесь, и топикстартер правильно говорит, что нужно писать ясный читаемый код, но почему-то делает упор в основном на производительность.
                • 0
                  Какая там проводится оптимизация, и проводится ли она, мне не ведомо (авторы забугорные и прямого доступа к ним у меня не было). Но для тех целей, для которых её создали, аппетиты избыточны изначально, не говоря про протекания. Я привел пример с промышленных сред лишь потому, что вы слишком яро отрицаете влияние казалось бы таких мелочей. Часто в проме используются системы, которые должны обрабатывать неимоверное количество запросов, так что на производительности сказывается всё.
                  • +1
                    Дело не в фанатичном отрицании как таком. Возможно, я был не совсем правильно понят. Я в своих комментариях хотел указать на две вещи. Во-первых, все надо делать постепенно. Во-вторых, не нужно смешивать все в одну кучу.

                    Если мы говорим об оптимизации готового приложения на Java будь то веб, пром, игры, андроид, нужно заниматься ей сверху вниз. Брать профайлер, анализировать логи и поведение, находить узкие места, думать над логикой, переписывать её для оптимизации, тестировать, забывать. В 99,67% случаев именно такой подход необходим и достаточен и дополнительных микрооптимизаций не требуется. В случае работы над новым проектом или кодом, нужно принимать во внимание потенциальные проблемы с производительностью на уровне опять же бизнес-логики, и всё.

                    То, что описано в статье, безусловно нужно делать, но при этом понимать, что делается это в целях повышения читабельности кода, приведения его к единому стандарту и т.д., но не делать упора на оптимизацию, так как в подавляющем большинстве случаев применение всех описанных практик не даст абсолютно никакого прироста производительности.
                    • 0
                      Собственно вывод об этом и говрит. Извиняюсь, если сложилось впечетление из статьи, что эти шаги — оптимизация производительности
    • +1
      Я например пишу сейчас приложение (не промышленное), в котором любой автобоксинг подобен смерти. Приходится за всеми этими «штучками» внимательно следить. Собственно, как уже сказали выше, пост написан не только для ентерпрайза, конечно.
      • 0
        И вы все описанные в статье штучки прекрасно знаете, не так ли? :)

        • 0
          Из статьи — все. Но из комментариев кое-что для себя почерпнул.
          Честно говоря не понял, к чему вопрос.
          • +2
            К тому, что эта статья — не руководство по оптимизации, о чем я вначале и написал. Начинающим надо начинать не с этого, а тем, кто немного разбирается в предмете, все эти мелочи известны. Хотел сказать, что описанные вопросы надо рассматривать в другом контексте. Ну да хватит наверное захламлять топик одним и тем же.
  • +2
    так как в StringBuilder нету синхронизированных методов в отличие от StringBuffer и следовательно производительность будет выше, хоть и не значительно.
    Это фактически бесполезное опасение, при однопоточном доступе лишняя синхронизация отлично оптимизируется компилятором по крайней мере до lightweight locking, а то и до пропуска блокировки (в данных примерах скорее всего). В любом случае это точно не то, что программисту стоит первым делом оптимизировать.
    можно увеличить кеш для Integer через системное свойство «java.lang.Integer.IntegerCache.high», а так же через параметр виртуальной машины -XX:AutoBoxCacheMax=Тут нужно сказать, что речь только о 7 версии, ибо до сих пор это было захардкожено внутри java.lang.Integer. Думаю, для многих это сюрприз :)
    • +2
      Блин, косяк с цитированием, сорри. Вообще, так настойчиво советовать в статьях такие вещи, я считаю, зло. Разумеется, знать эти тонкости очень полезно. Но если человек уже знает, статья ему ни к чему, а если не знает, то может всё не так понять. Каждый пункт тут — сомнителен и палка о двух концах. Такими вещами надо заниматься, если точно понимаешь что происходит. И тогда уж с Вашей стороны надо куда как подробнее расписать всё. Потому как зачастую странные вещи можно обнаружить. Вот помимо сказанного выше взять даже первый безапеляционный пункт:
      //медленно
      Integer i = new Integer(100);
      ...
      //быстро
      Integer i = Integer.valueOf(100);
      Что думает читатель? «Ага, valueOf быстрее, чем new». Верно думает читатель? Нет. Оно быстрее в общем случае только если в скобках определённые числа. Если мы напишем так:
      //медленно
      Integer i = new Integer(200);
      ...
      //быстро
      Integer i = Integer.valueOf(200);
      , то это уже, очевидно, неправда — ведь всё противоположно, не так ли? Первый отрезок быстрее второго. Да, быстрее на три с половиной тика, но раз уж взялись экономить на спичках — договаривайте до конца :)
      • 0
        то это уже, очевидно, неправда

        Зависит всетаки от значения IntegerCache.high, а не от кода
        • 0
          Верно, по сути так и есть. Есть какая-то граница, где в вашем коде правда превращается в неправду. И для одних чисел первый код быстрее (мы же, как я понял, только скорость сейчас оптимизируем), ибо используется кеш, а для других — второй код быстрее, ибо не производятся как минимум две проверки на границы кеша перед неизбежным последующим new(i). Но, во-первых, очевидно, что слишком сдвигать эту границу почти никогда нецелесообразно. Я с трудом могу придумать случаи, когда нужно взять и сдвинуть этот кеш куда-то далеко вправо. А во-вторых, речь всё же о каком-никаком общем случае.
      • 0
        Эти советы могут пригодиться людям пишущим

        (new Integer(x)).toString()

        вместо

        String.valueOf(x)
        • +1
          Integer.toString(x)
    • 0
      речь только о 7 версии

      Это не так. Метод
      getAndRemoveCacheProperties
      • 0
        Снова извиняюсь. Да, наврал чуть-чуть. Отстал на два-три релиза с шестой веткой оракловской, очень они резко двинулись. Скачал последний апдейт Oracle JDK — действительно, valueOf( int ) использует IntegerCache.high. Совсем недавно там было совершенно точно хардкод 128. Проверил в свежей OpenJDK — всё так и есть, всё тот же старый хардкод 128.
  • –2
    Почему только Integer.valueOf(int)? Попробуйте так:
    Integer i1 = new Integer(10);
    Integer i2 = new Integer(10);
    System.out.println(i1==i2);
    В результате true, т.к. созданные таким образом объекты тоже кэшируются.
    • 0
      Вы сейчас очень не правы, хотя в контексте вашей JVM это может быть правдой. Но я бы не полагался на специфику реализации.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +2
          Этот пункт только для boxing. Соответственно при создании нового объекта не должна выполняться данная часть спеки.
          • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        Прошу прощения, ввёл и себя и других в заблуждение. В примере должно быть Integer i1 = 10; Integer i2 = 10; И тогда по спецификации java они должны ссылаться на один и тот же объект для всех чисел из [-128, 127]
        • 0
          Просмотрите пожалуйста еще раз топик. Так как раз указано, что:
          Long l = 100L;//это тоже самое что Long.valueOf(100L);
      • 0
        О какой низкоуровневой оптимизации можно вообще говорить без привязки к JVM?
  • +1
    >null vs empty
    >
    > Всегда старайтесь в методах вашей бизнес логики возвращать пустые коллекции вместо null значений, это избавляет от лишних null-проверок и делает код чище.


    Во-первых, таким образом получится избавиться только лишь от небольшого количества проверок на null, в частности, в некоторых приватных методах. В пабликах и т.д. в любом случае придется проверки на null делать, так как заранее можете и не знать, кто и как будет их вызывать в будущем.

    Во-вторых, использование пустых коллекций и прочих объектов вместо null отрицательно скажется на производительности за счет расходов на сериализацию например при ремотных вызовах, а также увеличит трафик (больший объем сериализованных данных будет передаваться), также увеличит потребление памяти, и возможно, диска, если сериализованные данные будут где-то храниться, и т.д.

    В общем, я, конечно, не призываю во всех случаях использовать null, но призываю воздерживаться от вырадения «всегда старайтесь делать то-то» без указания контекста.
    • 0
      Во-первых, таким образом получится избавиться только лишь от небольшого количества проверок на null, в частности, в некоторых приватных методах. В пабликах и т.д. в любом случае придется проверки на null делать, так как заранее можете и не знать, кто и как будет их вызывать в будущем.

      Ну это уже от контракта ваших методов зависит. Некоторые считают нормальным подразумевать, что методы не допускают ввода null в качестве параметра и выбрасывать исключение времени выполнения.
      • 0
        Это понятно, что от контракта зависит. Но автор указал, что одна из его целей использования пустых коллекций – избавиться от по его мнению лишних проверок на null. Из чего делаем закономерный вывод, что в его случае подобного контракта не предусметрено (иначе вообще не было бы смысла упоминать null).
    • 0
      >а также увеличит трафик (больший объем сериализованных данных будет передаваться)

      Поправьте меня, если я неправ, но при использовании JSON (который по-моему уже стандарт де-факто для сетевой передачи данных) будет следующая картина:

      {
      ...
      some_list:[],
      ...
      }

      против
      {
      ...
      some_list:null,
      ...
      }

      Разница в 2 байта, причём в пользу пустого списка ;)
      • +1
        зависит от маршаллера — поле может в обоих случаях вообще отсутствовать
  • +1
    Хотелось бы увидеть доказательства, что после JIT действительно будет хоть какойто выигрыш? Скажем, я лично не уверен, что new String("...") и «ююю» вообще компилируется в разные вещи. Я конечно тут гляну на досуге, но без веских доказательств мне сложно поверить, что будет разница после JIT во всех этих преобразованиях в Integer, String итп.
    Скажем «Преобразование чисел» предложение оно вообще непонятно как может быть скомпилировано иначе чем в valueOf. Если там будет отличие в байт коде мне это будет удивительно. Сам активно предпочитаю + "", изза лаконичности и лучшей читабельности.
    Collections.emptyMap и тому подобные методы (Arrays.asList скажем) это очень опасная штука ибо неявно immutable. Причем код понять это иначе чем получив эксепшен не может. Что впрочем вообще общая проблема паттерна null object, относительно безопасно отдавать его только как Iterable<...>.
    Все отсылки к идеологии ООП, тоже слабый аргумент. Если конечно это не академическое программирование. Я не вижу практической пользы набор констант оформлять как класс и уж бы точно не стал бы советовать такую практику новичку. И воспринимать ООП излишне серьезно я бы тоже крайне не советовал, это довольно вредное заблуждение. Программирование должно быть прагматично, и исходить где лучше применить наследование, делегирование или композицию лучше не из формальных правил ООП, а из соображений удобства. Скажем, в некоторых случаях наследования стоит избегать где только это возможно изза непредсказуемых поведений потомком при изменении родителя.

    Про отличия стринг билдера и буфера, это вообще очень странный совет, синхронизация дальше BIAS не надуется и на производительности скажется не более, чем сверка threadId с монитором. Сомневаюсь, что это сильный аргумент. Хотя, конечно билдер использовать более правильно, но на перформанс кивать тут довольно глупо, если это не мегаострое место в программе.

    Боятся instanceof тоже не стоит, где-то была работа где изучались разные виды multiple dispatch. Все эти двойные коллбеки аля визитор, instanceof и прочие полиморфизмы. instanceof по перформансу был одним из самых быстрых. Опять же проблема тут не в перформансе, а скорее в поддерживаемости кода. Тормознутость рефлекшена, кстати опять же сильно преувеличена, при умелом подходе оно бегает довольно шустро.
    • +1
      Вот статья про multipledispatch — habrahabr.ru/blogs/java/118087/
    • +2
      private static String x() {
      	return "...";
      }
      
      private static String y() {
      	return new String("...");
      }
      
      private static java.lang.String x();
        Code:
         0:	ldc	#2; //String ...
         2:	areturn
      
      private static java.lang.String y();
        Code:
         0:	new	#3; //class java/lang/String
         3:	dup
         4:	ldc	#2; //String ...
         6:	invokespecial	#4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
         9:	areturn
      
      }
      
      
    • +2
      Скажем, я лично не уверен, что new String("...") и «ююю» вообще компилируется в разные вещи.

      Любая конструкция «foobar» является частью константного пула класса. Вызов «new String(»foobar")" порождает новый объект, который использует соответствующую константную строку. Байткод в комментарии выше это наглядно илюстрирует.

      Скажем «Преобразование чисел» предложение оно вообще непонятно как может быть скомпилировано иначе чем в valueOf.

      Boxing будет в любом случае проходить через valueOf(), однако вызов «new Integer(n)» javac-компилятор соптимизировать не сможет, т.к. иначе не будет выполняться (new Integer(0) != new Integer(0)), что будет противоречить спецификации языка.

      Про отличия стринг билдера и буфера, это вообще очень странный совет, синхронизация дальше BIAS не надуется и на производительности скажется не более, чем сверка threadId с монитором. Сомневаюсь, что это сильный аргумент. Хотя, конечно билдер использовать более правильно, но на перформанс кивать тут довольно глупо, если это не мегаострое место в программе.

      Не понимаю, почему вы считаете это «странным» советом. Применение синхронизации не к месту может действительно привести к неожиданной синхронизации потоков, когда этого можно было бы избежать. В случае со StringBuilder/StringBuffer это кажется маловероятным, но я не вижу причин, по которым можно себе прощать использование буффера вместо билдера из-за того, что «немного кода синхронизации погоды не сделают».

      Боятся instanceof тоже не стоит, где-то была работа где изучались разные виды multiple dispatch...

      Бояться — нет. Избегать, где это возможно — да. Оптимизации instanceof в HotSpot хоть и хороши, тем не менее не являются серебряной пулей — против них можно легко можно создать контр-пример. В реальных приложения такие контр-примеры случаются не часто. Но в хорошо-спроектированных реальных приложениях «instanceof» в принципе почти не встречается.

      Тормознутость рефлекшена, кстати опять же сильно преувеличена, при умелом подходе оно бегает довольно шустро.

      Ключевая фраза «при умелом подходе». Опять же, неиспользование рефлексии, где использования можно избежать, всегда быстрее, чем ее использование.

      Тем не менее, ваш поинт о том, что использование различных механизмов (рефлексия, instanceof) не станет резким образом узким местом в производительности, более чем справедлив.
      • +1
        В целом согласен. Только в случае с баффером-билдером так и непонятно, в чем преимущество последнего, кроме синхронизации, которая вообще незаметна при однопоточном использовании и дает надежность против легкого удара по перформансу в многопоточном использовании.
        • 0
          Синхронизация дает значительный (относительно) удар даже при однопоточном выполнении.
          • 0
            Синхронизация дает значительный (относительно) удар даже при однопоточном выполнении.
            Это не совсем так, выше я написал отчего. Не даёт синхронизация никакого удара при однопоточном выполнении. По крайней мере если конкретно говорить о HotSpot. А мы, судя по статье и комментариям автора говорим именно о ней. Если не говорить конкретно о какой-то машине, то практически ни один из данных советов не имеет какого-то смысла.
          • 0
            Поподробнее пожалуйста. Какой удар вы имеете ввиду от биасед синхронизации?
      • 0
        Кстати, стоит провести поиск по сорцам JDK как находятся тысяча использовании new Integer(0) и тому подобных. Например в классе Locale, MessageFormat. В сорцах спринга тоже встречается. Это банально чище выглядит в коде, нагляднее. Так что практика довольно распространенная, насколько она вредна сложно судить без тестов.
  • +4
    Можете ещё дописать:

    1. Ели вы будете затем итерироваться по отображению/множеству, используйте LinkedHashMap/LinkedHashSet вместо HashSet/HashMap, поскольку для операция next() итератора существенно быстрее. Кроме того, порядок итерации получается такой же, в котором объекты были добавлены.

    2. Не инкрементируйте wrapper-ы примитивов в цикле, поскольку это порождает создание новых объектов:
    public void incrementInteger() {
            for(Integer i = 0; i <= 9000; i ++) {
                //do something
            }
        }
    
        public void incrementInt() {
            for(int i = 0; i <= 9000; i ++) {
                //do something
            }
        }
    

    Генерирует следующие инструкции:
    public void incrementInteger();
      Code:
       0:	iconst_0
       1:	invokestatic	#2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4:	astore_1
       5:	aload_1
       6:	invokevirtual	#3; //Method java/lang/Integer.intValue:()I
       9:	sipush	9000
       12:	if_icmpgt	34
       15:	aload_1
       16:	astore_2
       17:	aload_1
       18:	invokevirtual	#3; //Method java/lang/Integer.intValue:()I
       21:	iconst_1
       22:	iadd
       23:	invokestatic	#2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       26:	dup
       27:	astore_1
       28:	astore_3
       29:	aload_2
       30:	pop
       31:	goto	5
       34:	return
    
    public void incrementInt();
      Code:
       0:	iconst_0
       1:	istore_1
       2:	iload_1
       3:	sipush	9000
       6:	if_icmpgt	15
       9:	iinc	1, 1
       12:	goto	2
       15:	return
    }
    
    • +3
      > Ели вы будете затем итерироваться по отображению/множеству, используйте LinkedHashMap/LinkedHashSet вместо HashSet/HashMap, поскольку для операция next() итератора существенно быстрее.

      Замеры в студию!

      > Кроме того, порядок итерации получается такой же, в котором объекты были добавлены.
      А вот это уже совсем другое требование.

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

      Если нет требований к порядку итератора — надо юзать HashMap вместо LinkedHashMap.
      Еще веселее — если нет требований к порядку итератора не нужно использовать SortedMap/SortedSet там где можно обойтись HashMap и т.д.

      Ну а самое главное — LinkedHashMap не имеет аналога в java.util.concurrency, что может полностью сломать попытки улучшить скалабильность. HashMap (с точностью до приседаний вокруг null key/value) легко заменяется на ConcurrentHashMap.

      • +1
        Да, насчёт многопоточности согласен — минус есть. Насчёт наложения ограничений по поводу пордяка итерации — не согласен. Если приложению плевать, в каком порядке будет итерация, то от того, что она окажется в том же, в котором элементы добавлялись, хуже не станет.

        Замеры я делал, но это было довольно давно, и они не сохранились. Результаты, естественно, подтверждают мой прошлый комментарий, иначе бы я его не написал. Более того, кэп утверждает, что в данном случае замеры не особо-то и нужны, достаточно знать, как реализованы эти структуры данных. В Linked получение следующего элемента происходит за чистую O(1), с такой же скоростью, как и у LinkedList. В обычной же хеш-таблице нужно ещё бежать по бакетам, пока не встретится бакет с элементом. В среднем, конечно, тоже получается O(1), но эта O — амортизированная, а не чистая, что совсем не одно и то же.
        • +2
          Зато добавление/удаление дороже. Вот и возникает вопрос — а оно надо?
          Не нужно делать ничего пока оно не надо! ;)
        • +1
          > Более того, кэп утверждает, что в данном случае замеры не особо-то и нужны, достаточно знать, как реализованы эти структуры данных.

          Но в данном случае майор знает, что капитан не прав. Делать умозрительные (без измерений) заключения о перформансе — это в 98% случаев попасть мимо.

          Например можно сделать тест где итерация по LinkedHashMap будет медленее чем по HashMap так как из-за ограничений на порядок элементов получим большее количиство cache miss. Но этот тест также будет сферическим конем.
      • 0
        LinkedHashMap можно заменить ConcurrentSkipListMap, в котором ключами являются врапперы ключей и сравнением по времени создания :)
        • 0
          Ааааааааааааааааааааааа.
          Ужос ужос ужос.
          Не используйте SortedMap, там где Sorted не нужен. ;)
  • +6
    Наезд на instanceOf неверен принципиально — перелопачивать архитектуру в обмен на ~0 прирост производительности.
    • 0
      instanceOf — не ООП-подход.

      Остальные аргументы слабее в данном случае.
  • +2
    null vs empty — неверно с точки зрения производительности. Проверка на нулл быстрее проверки на ноль. Так что правильным решением будет не проверять на пустоту, и не возвращать пустых коллекций.

    collection == null просто проверит адреса. Операцию быстрее этой придумать сложно.
    collection.isEmpty() сперва проверит на null (чтобы вернуть NPE если таки да), а потом вызовет метод isEmpty

    Кстати, проверки вроде collecrion.size() == 0 это наихудший вариант, ибо далеко не для всех коллекций size выполняется за фиксированное время.

    Преобразование чисел — тут фокус в том, что это по сути вызовет все тот же valueOf (вот не знаю точно, как эмулируется toString для примитивов), а потом конкатенацию. Но компилятор вполне может и оптимизировать, увидев что там пустая строка, и тогда получится шило на мыло.
    • 0
      Сравните:
      for(Item i: ItemCollector.getItems()) {
      ...
      }
      ...

      и

      List items = ItemCollector.getItems();
      if(items != null) {
      for(Item i: items) {
      ...
      }
      }
      ...

      Эта рекомендация не для повышения производительности, а для повышения читабельности.
  • 0
    Может я что-то упустил, но в исходниках класса java.lang.Long метод valueOf(String) определяется как

        /**
         * Returns a <code>Long</code> object holding the value
         * of the specified <code>String</code>. The argument is
         * interpreted as representing a signed decimal <code>long</code>,
         * exactly as if the argument were given to the {@link
         * #parseLong(java.lang.String)} method. The result is a
         * <code>Long</code> object that represents the integer value
         * specified by the string.
         * <p>
         * In other words, this method returns a <code>Long</code> object
         * equal to the value of:
         *
         * <blockquote><pre>
         * new Long(Long.parseLong(s))
         * </pre></blockquote>
         *
         * @param      s   the string to be parsed.
         * @return     a <code>Long</code> object holding the value
         *             represented by the string argument.
         * @exception  NumberFormatException  If the string cannot be parsed
         *              as a <code>long</code>.
         */
        public static Long valueOf(String s) throws NumberFormatException
        {
    	return new Long(parseLong(s, 10));
        }
    


    Другими словами Long.valueOf сводиться к new Long. Каким образом он может быть быстрее?
    • +4
      Прошу прощения, проглядел, что и Long и Integer также имеют перегруженный метод для примитивного аргумента

          /**
           * Returns a <tt>Long</tt> instance representing the specified
           * <tt>long</tt> value.
           * If a new <tt>Long</tt> instance is not required, this method
           * should generally be used in preference to the constructor
           * {@link #Long(long)}, as this method is likely to yield
           * significantly better space and time performance by caching
           * frequently requested values.
           *
           * @param  l a long value.
           * @return a <tt>Long</tt> instance representing <tt>l</tt>.
           * @since  1.5
           */
          public static Long valueOf(long l) {
      	final int offset = 128;
      	if (l >= -128 && l <= 127) { // will cache
      	    return LongCache.cache[(int)l + offset];
      	}
              return new Long(l);
          }
      


      который как раз использует кеширование.
  • +1
    Очередная ситуация, где коментарии интереснее самого топика.
  • НЛО прилетело и опубликовало эту надпись здесь
  • –2
    Насчёт медленного new для простых объектов это уже миф. Создание простого объекта — инкремент указателя, удаление из young — тоже почти бесплатное.
    Не поленился, написал тест

    public class NewMain {

    static final long iterations = 10000L * 1000000L;

    public static void main(String[] args) {
    Runnable doNew = new Runnable() {
    public void run() {
    int s = 0;
    for (long i = 0; i < iterations; ++i) {
    Integer v = new Integer(100);
    s += v;
    }
    }
    };
    Runnable doValue = new Runnable() {
    public void run() {
    int s = 0;
    for (long i = 0; i < iterations; ++i) {
    Integer v = Integer.valueOf(100);
    s += v;
    }
    }
    };
    measure("valueOf", doValue);
    measure("new", doNew);
    measure("valueOf", doValue);
    measure("new", doNew);
    }

    private static void measure(String msg, Runnable r) {
    long start = System.currentTimeMillis();
    r.run();
    long ela = System.currentTimeMillis() - start;
    System.out.println(msg + " elapsed " + ela + ", rps " + (iterations / ela) * 1000);
    }

    }


    Запускаем на разных jvm

    alexm-nb:~/tmp/pt$ java -version
    java version «1.6.0_20»
    OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.10.2)
    OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)
    alexm-nb:~/tmp/pt$ java NewMain
    valueOf elapsed 11752, rps 850918000
    new elapsed 7763, rps 1288161000
    valueOf elapsed 11542, rps 866400000
    new elapsed 7757, rps 1289158000

    alexm@adams:~/pt$ java -version
    java version «1.6.0_29»
    Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
    Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode)
    alexm@adams:~/pt$ java NewMain
    valueOf elapsed 12594, rps 794028000
    new elapsed 8396, rps 1191043000
    valueOf elapsed 12616, rps 792644000
    new elapsed 8389, rps 1192037000

    Привет, new быстрее valueOf. Если сделать valueOf(200), чтобы оно не лезло в кеш, будет одинаково быстро.
    • –1
      Ну вы даете, запустили 4 конкурентных потока и ожидаете получить правдивые результаты? Сделайте хотя бы так:
      measure("valueOf", doValue);
      Thread.yield();
      measure("new", doNew);
      Thread.yield();
      measure("valueOf", doValue);
      Thread.yield();
      measure("new", doNew);


      Вот результат:
      valueOf elapsed 3006, rps 332667000
      new elapsed 10259, rps 97475000
      valueOf elapsed 2912, rps 343406000
      new elapsed 10268, rps 97389000

      Как раз почти 3.5 раза
      • 0
        Где вы увидели конкурентные потоки?
        • +1
          Сглупил, извиняюсь. Ваш код — как раз пример того как делать не надо. Увидел Runnable и подсознательно приписал new Thread®.start();
    • 0
      Проблема с простыми объектами, если они не просто создались, а пожили некоторое небольшое время и после этого освободились. Тут они вполне могут случайно пережить 2-3 минор коллекции и угодить в олд ген. Если приложение серверное (как у меня) и живет длительное время и не может позволить себе задержки от мажор коллекций, это большая проблема со временем. Хотя Оракл активно и пропагандирует, что создание pojo операция копеечная, это все еще бывает заметной проблемой.
    • 0
      Тест крайне синтетический. Да, в данном виде придраться не к чему, написано грамотно. Но по сути никакой реальной картины не даёт. Не поленился, запустил на Java HotSpot(TM) Server VM (build 20.4-b02, mixed mode):
      valueOf elapsed 19938, rps 501554000
      new elapsed 18235, rps 548395000
      valueOf elapsed 19242, rps 519696000
      new elapsed 20556, rps 486475000
      Это показывает только, что есть вероятная (!) разница порядка секунды на 10 миллиардах (!) итераций.
  • 0
    >>Старайтесь использовать @Override аннотацию для методов, которые переопределяют методы супер классов.

    В седьмой джаве, можно (и нужно!) и имплементируемые методы интерфейсов так аннотировать.
    • 0
      Это начиная с 6й явы так.
      • 0
        Пардон, был взволнован.
  • 0
    > //медленно
    > int a = 12;
    > String s = a + "";
    > //быстро
    > String s = String.valueOf(12);

    Проще будет, раз уж итак константа:
    String s = «12»;

    Видимо автор имел в виду:
    String s = String.valueOf(a);
  • 0
    Огромное спасибо за статью, прочитав ее увидел ошибки, которые встречаются у меня в коде, и благодаря Вашей статье, уже постараюсь больше не допускать, еще раз спасибо за труд!
  • 0
    Ну и java.lang.String.intern() до кучи
    • +2
      интерн поможет спасит от инлайна при компиляции,
      но может печально сказаться на производительности:

      подробное описание

      так что с этим нужно тоже быть достаточно аккуратно.

      p.s. на седьмой jdk не проверял, но последняя 6я от оракла печально себя ведет.
      • 0
        Очень познавательно, спасибо.
  • 0
    Collections.emptyList(); — Хозяйке на заметку.
  • 0
    Есть еще полезный прием:
    ''true''.equals(str)
    Позволяет избежать NPE при сравнении строк с константами.
  • 0
    //плохо
    interface A {
        public static final String A = "a";
    }
    

    Это не только плохо, но и криво. По умолчанию, все поля интерфейса публичные, финальные и статические.

    //аналогично плохо
    interface A {
        String A = "a";
    }
    

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