'...!= null ' или ' null!= ... 'лучшая производительность?

Я написал два метода, чтобы проверить там работоспособность

 public class Test1 {

 private String value;

 public void notNull(){
  if( value != null) {
    //do something
  }
}

public void nullNot(){
 if( null != value) {
  //do something
 }
}

}

и проверил его байтовый код после компиляции

public void notNull();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: getfield #2; //Field value:Ljava/lang/String;
4: ifnull 7
7: return
LineNumberTable: 
line 6: 0
line 9: 7

StackMapTable: number_of_entries = 1
frame_type = 7 /* same */


public void nullNot();
Code:
Stack=2, Locals=1, Args_size=1
0: aconst_null
1: aload_0
2: getfield #2; //Field value:Ljava/lang/String;
5: if_acmpeq 8
8: return
LineNumberTable: 
line 12: 0
line 15: 8

StackMapTable: number_of_entries = 1
frame_type = 8 /* same */


}

здесь два опкода используются для реализации условия if: в первом случае он использует ifnull-check верхнее значение стека равно null -, а во втором случае он использует if_acmpeq-check два верхних значения равны в стеке -

Итак, это повлияет на производительность? (это поможет мне доказать, что первая реализация null хороша в производительность мудрая, а также в аспекте читаемости :))

15 ответов


сравнение сгенерированных байт-кодов в основном бессмысленно, так как большая часть оптимизации происходит во время выполнения с компилятором JIT. Я собираюсь догадаться, что в этом случае любое выражение одинаково быстро. Если и есть какая-то разница, то незначительная.

Это не то, о чем вам нужно беспокоиться. Ищите оптимизацию общей картины.


не оптимизируйте за счет читаемости, если скорость (или память/в любом случае) будет незначительной. Я думаю !=null обычно более читаем, поэтому используйте это.


С такими вопросами трудно понять, насколько умным будет JVM (хотя ответ "обычно довольно умный, если это возможно", и в этом случае это выглядит очень возможным). Но на всякий случай проверьте:--3-->

class Nullcheck {
  public static class Fooble { }

  Fooble[] foo = {null , new Fooble(), null , null,
                  new Fooble(), null, null, new Fooble() };

  public int testFirst() {
    int sum = 0;
    for (int i=0 ; i<1000000000 ; i++) if (foo[i&0x7] != null) sum++;
    return sum;
  }

  public int testSecond() {
    int sum = 0;
    for (int i=0 ; i<1000000000 ; i++) if (null != foo[i&0x7]) sum++;
    return sum;
  }

  public void run() {
    long t0 = System.nanoTime();
    int s1 = testFirst();
    long t1 = System.nanoTime();
    int s2 = testSecond();
    long t2 = System.nanoTime();
    System.out.printf("Difference=%d; %.3f vs. %.3f ns/loop (diff=%.3f)\n",
      s2-s1,(t1-t0)*1e-9,(t2-t1)*1e-9,(t0+t2-2*t1)*1e-9);
  }

  public static void main(String[] args) {
    Nullcheck me = new Nullcheck();
    for (int i=0 ; i<5 ; i++) me.run();
  }
}

и на моей машине это дает:

Difference=0; 2.574 vs. 2.583 ns/loop (diff=0.008)
Difference=0; 2.574 vs. 2.573 ns/loop (diff=-0.001)
Difference=0; 1.584 vs. 1.582 ns/loop (diff=-0.003)
Difference=0; 1.582 vs. 1.584 ns/loop (diff=0.002)
Difference=0; 1.582 vs. 1.582 ns/loop (diff=0.000)

таким образом, ответ: нет, никакой значимой разницы вообще. (И компилятор JIT может найти дополнительные трюки, чтобы ускорить каждый после того же количества повторов работает.)


обновление: приведенный выше код работает специальный тест. Используя JMH (теперь, когда он существует!) является хорошим способом, чтобы помочь избежать (некоторые) подводные камни microbenchmarking. Приведенный выше код позволяет избежать худших ловушек, но не дает явных оценок ошибок и игнорирует различные другие вещи, которые иногда имеют значение. В эти дни: используйте JMH! Кроме того, когда вы сомневаетесь, запустите свои собственные критерии. Детали иногда имеют значение - не очень часто для чего-то столь же простого как это, но если это действительно важно для вас, вы должны проверить состояние как можно ближе к производству.


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

обычно тело функции будет использовать только несколько переменных, и обычно очевидно в контексте, какая переменная находится под контролем. Ставя константу слева, мы более близко мимик!--3--> и case: дано этой переменная, выберите соответствующее значение. Видя значение слева, вы фокусируетесь на конкретном выбранном условии.

когда я проверить

if (var == null)

Я прочитал это как: "мы проверки var вот, и мы сравниваем его для равенства, против ... а, ноль.- И наоборот, когда я сканирую!--10-->

if (null == var)

Я думаю :"мы видим, если значение равно null, и ... да, это var мы проверять."Это даже более сильное признание с

if (null != var)

который мой глаз сразу же подхватывает.

видя байткод разница завораживала. Спасибо, что поделился.


разница будет незначительной, поэтому идите с тем, что наиболее читаемо (!= null ИМО)


Я бы придерживался (значение != null) для удобства чтения. Но вы всегда можете использовать утверждения.


минутная оптимизация, как это работа компилятора, особенно на языках высокого уровня, таких как Java.

хотя строго это не актуально здесь, не оптимизируйте преждевременно!


положить null сначала создается дополнительный байт-код, но кроме этого может не быть разницы в производительности.

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

Я бы использовал notNull() подход, так что вы не бросаете ошибку компилятора, если вы забыли ! и случайно типа null = value.


О, если вы просите максимальную производительность, не создавайте дополнительный класс или методы. Даже статические методы заняли бы немного времени, поскольку загрузчику классов Java нужно JIT загрузить его.

поэтому, когда вам нужно проверить, является ли переменная нулевой, вы просто проверяете ее либо

if (x == null)

или

if (null == x)

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


вы можете игнорировать этот очень мелкий материал оптимизации во время кодирования


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


С точки зрения, нет существенной разницы в производительности.

однако полезно сначала написать null, чтобы поймать ошибки опечаток.

например, если вы привыкли писать этот код:

if (obj == null)

может быть написано по ошибке как:

if (obj = null)

С точки зрения компилятора, это нормально.

однако, если вы привыкли писать код как:

if (null == obj)

и сделали ошибку пиши:

if (null = obj)

компилятор сообщит вам, что вы сделали ошибку в этой строке.


в Java-8 два дополнительных метода были представлены Objects класс: объекты#notNull и объекты#isNull, который вы можете использовать, чтобы заменить null проверка. Интересно, что оба они сначала используют объекты:

public static boolean isNull(Object obj) {

      return obj == null;

} 

и

public static boolean nonNull(Object obj) {

        return obj != null;

}

соответственно. Я думаю, это означает, что это рекомендуемый способ (по крайней мере, разработчики core jdk использовали этот подход) объекты, исходный код


Я бы использовал" новую " функцию Java 8, я пишу несколько примеров:

import java.util.Optional;

public class SillyExample {

public void processWithValidation(final String sampleStringParameter){
    final String sampleString = Optional.ofNullable(sampleStringParameter).orElseThrow(() -> new IllegalArgumentException("String must not be null"));

    //Do what you want with sampleString
}


public void processIfPressent(final String sampleStringParameter){
    Optional.ofNullable(sampleStringParameter).ifPresent(sampleString -> {

        //Do what you want with sampleString

    });

}

public void processIfPressentWithFilter(final String sampleStringParameter){
    Optional.ofNullable(sampleStringParameter).filter("hello"::equalsIgnoreCase).ifPresent(sampleString -> {

        //Do what you want with sampleString

    });

}

}


байтовый код - это просто простой перевод исходного кода.