Низкая производительность многих операторов if-else в Java - PullRequest
8 голосов
/ 25 октября 2011

У меня есть метод, который проверяет все комбинации 5 различных условий с 32 операторами if-else (вспомним таблицу истинности).5 различных букв представляют методы, каждый из которых выполняет свои собственные регулярные выражения в строке и возвращает логическое значение, указывающее, соответствует ли строка регулярному выражению.Например:

if(A,B,C,D,E){

}else if(A,B,C,D,!E){

}else if(A,B,C,!D,!E){

}...etc,etc.

Однако это действительно влияет на производительность моего приложения (извините, я не могу вдаваться в подробности).Кто-нибудь может порекомендовать лучший способ обработки такой логики?

Каждый метод, использующий регулярное выражение, выглядит следующим образом:

String re1 = "regex here";
Pattern p = Pattern.compile(re1, Pattern.DOTALL);
Matcher m = p.matcher(value);
return m.find();

Спасибо!

Ответы [ 10 ]

18 голосов
/ 25 октября 2011

Вы можете попробовать

boolean a,b,c,d,e;
int combination = (a?16:0) + (b?8:0) + (c?4:0) + (d?2:0) + (e?1:0);
switch(combination) {
   case 0:
        break;
   // through to
   case 31:
        break;
}
8 голосов
/ 25 октября 2011

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

int result = 0;
if(A) {
  result |= 1;
}
if(B) {
  result |= 2;
}
// ...

switch(result) {
  case 0: // (!A,!B,!C,!D,!E)
  case 1: // (A,!B,!C,!D,!E)
  // ...
}
3 голосов
/ 25 октября 2011

Все вышеприведенные ответы неверны, потому что правильный ответ на вопрос оптимизации: Измерьте! Используйте профилировщик, чтобы измерить, где ваш код тратит свое время.

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

Я также готов поспорить с предложениями jtahlborn и Peter Lawrey's и Salvatore Previti (по сути, одинаковыми), хотя они и будут умными, принесут вам незначительную дополнительную выгоду, если только выработающий на 6502 ...

(Этот ответ звучит так, как будто я им полон, поэтому в интересах полного раскрытия я должен упомянуть, что я на самом деле безнадежен в оптимизации. Но измерение все же является правильнымответить.)

3 голосов
/ 25 октября 2011

Не зная больше деталей, может быть полезно расположить операторы if таким образом, чтобы те, которые выполняют "тяжелую" обработку, выполнялись последними.Это делает предположение, что другие условия будут верны, тем самым избегая «тяжелых» подъемов все вместе.Короче говоря, воспользуйтесь короткими замыканиями, если это возможно.

2 голосов
/ 25 октября 2011

У меня есть решение с EnumSet. Однако это слишком многословно, и я думаю, что предпочитаю решение @Peter Lawrey.

В Effective Java by Bloch рекомендуется использовать EnumSet для битовых полей, но я бы сделал здесь исключение. Тем не менее я опубликовал свое решение, потому что оно может быть полезно для кого-то с немного другой проблемой.

import java.util.EnumSet;

public enum MatchingRegex {
  Tall, Blue, Hairy;

  public static EnumSet<MatchingRegex> findValidConditions(String stringToMatch) {
     EnumSet<MatchingRegex> validConditions = EnumSet.noneOf(MatchingRegex.class);
     if (... check regex stringToMatch for Tall)
       validConditions.add(Tall);
     if (... check regex stringToMatch for Blue)
       validConditions.add(Blue);
     if (... check regex stringToMatch for Hairy)
       validConditions.add(Hairy);
     return validConditions;         
  }
}

и вы используете это так:

Set<MatchingRegex> validConditions = MatchingRegex.findValidConditions(stringToMatch);

if (validConditions.equals(EnumSet.of(MatchingRegex.Tall, MathchingRegex.Blue, MatchingRegex.Hairy))
   ...
else if (validConditions.equals(EnumSet.of(MatchingRegex.Tall, MathchingRegex.Blue))
   ...
else if ... all 8 conditions like this

Но это было бы более эффективно, как это:

if (validConditions.contains(MatchingRegex.Tall)) {
  if (validConditions.contains(MatchingRegex.Blue)) {
     if (validConditions.contains(MatchingRegex.Hairy)) 
        ... // tall blue hairy
     else
        ... // tall blue (not hairy)
  } else {
     if (validConditions.contains(MatchingRegex.Hairy)) 
        ... // tall (not blue) hairy
     else
        ... // tall (not blue) (not hairy)
} else {
      ... remaining 4 conditions
}
2 голосов
/ 25 октября 2011

Может быть, разбить его на слои, например:

if(A) {
    if(B) {
        //... the rest
    } else {
        //... the rest
    }
} else {
    if(B) {
        //... the rest
    } else {
        //... the rest
    }
}

Тем не менее, кажется, что должен быть лучший способ сделать это.

2 голосов
/ 25 октября 2011

Одно из возможных решений: использовать переключатель, создающий двоичное значение.

int value = (a ? 1 : 0) | (b ? 2 : 0) | (c ? 4 : 0) | (d ? 8 : 0) | (e ? 16 : 0);

switch (value)
{
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    ...
    case 31:
}

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

2 голосов
/ 25 октября 2011

Запустите регулярное выражение один раз для каждой строки и сохраните результаты в логические значения и просто выполните if / else для логических значений вместо того, чтобы запускать регулярное выражение несколько раз. Кроме того, если вы можете, попробуйте повторно использовать предварительно скомпилированную версию вашего регулярного выражения и используйте это снова.

0 голосов
/ 25 октября 2011

предварительная генерация A, B, C, D и E в виде логических значений вместо оценки их в блоках условий if обеспечит как удобочитаемость, так и производительность. Если вы также обеспокоены производительностью различных случаев, вы можете организовать их в виде дерева или объединить их в одно целое число (X = (A? 1: 0) | (B? 2: 0) | ... | ( E? 16: 0)), который вы бы использовали в switch.

0 голосов
/ 25 октября 2011

Вы также можете адаптировать свой if / else к переключателю / кейсу (который, как я понимаю, быстрее)

...