Spiral group

_________________________________________________________________________________

 

< Раздел “Язык Java > < Раздел “Технологии Java > < Основная страница >

 

Поиск одиночного символа в строке типа String. Java JDK 1.6.

 

Статья опубликована: 31.07.2006

Последнее обновление: 18.03.2007

 

 

Поиск одиночного символа в подстроке зачастую воспринимается как поиск подстроки, состоящей из одного символа в заданной строке. В подобной ситуации не ясно каким из методов можно воспользоваться: indexOf(String s) или indexOf(int ch) ?  Например, для поиска символа F можно воспользоваться записью indexOf("F") или indexOf('F').

В ряде статей советуется использование того или иного метода и не приводится ни одного теста. Некоторых программистов настораживает, что в роли аргумента выступает не переменная типа char а переменная типа int, которая может вместить любой код кодовой точки Unicode. И из-за отсутствия представления о кодовых точках Unicode, полная поддержка которых была введена в JDK 1.5, пользуются методом indexOf("F").

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

Следующая программа демонстрирует время поиска подстроки, состоящей их одиночного символа двумя способами:

— методом indexOf  класса String с аргументом типа String;

— методом indexOf  класса String с аргументом – символом (кодовая точка Unicode).

 

 

Файл SingleSymbolSearch.java

/**

 *  @author <a href="mailto:zagrebin_v@mail.ru"> Victor Zagrebin </a>.

 */

class SingleSymbolSearch

{

 public static void main(String args[])

 {

  int pos = 0;

  StringBuffer sb = new StringBuffer("CBABA");

  for(int i=0; i<10000000; i++)

  {

   sb.append("AAAAAABCADAAB");

  }

  sb.append("AAAAAAF");

  String word = "AAAAAAF";

  String str = sb.toString();

  System.out.println("Characters number in source string="+str.length());

  long start = System.currentTimeMillis();

  pos = str.indexOf("F");

  long end = System.currentTimeMillis();

  System.out.println("indexOf method with String character."+

" Elapsed time: " + (end-start)+" msec");

  System.out.println("indexOf method with String character."+

" Found string at position: "+pos);

  start = System.currentTimeMillis();

  pos = str.indexOf('F');

  end = System.currentTimeMillis();

  System.out.println("indexOf method with character (Unicode code point)."+

" Elapsed time: " + (end-start)+" msec");

  System.out.println("indexOf method with character (Unicode code point)."+

" Found string at position: "+pos);

 }

}

 

Компиляция:

 

javac SingleSymbolSearch.java

 

Запуск:

 

java Xmx1024M SingleSymbolSearch

 

Для запуска требуется наличие достаточного места в оперативной памяти, если выводится ошибка нехватки памяти, то можно уменьшить в цикле for(int i=0; i<10000000; i++) максимальное значение индекса в несколько раз. В этом цикле создается строка достаточно большого размера для проведения теста.

 

Результаты вывода программы:

 

Characters number in source string=130000012

indexOf method with String character. Elapsed time: 437 msec

indexOf method with String character. Found string at position: 130000011

indexOf method with character (Unicode code point). Elapsed time: 641 msec

indexOf method with character (Unicode code point). Found string at position: 130000011

 

В приведенной программе в качестве аргумента явно задается символ 'F' , который будет трансформирован компилятором в код Unicode (кодовую точку Unicode).

По результатам теста видно, что при поиске одиночного символа с помощью метода indexOf("F") вместо indexOf('F'), используя в качестве аргумента символ (кодовую точку Unicode), выигрыш по времени  может составлять 0% - 20% в зависимости от длины строки и  местоположения найденного символа. Средний выигрыш составляет 10%. Причину выигрыша по времени можно найти в исходных кодах класса String для семейства методов indexOf().

Опуская полный код класса String, ниже приводится код тех методов, которые задействуются при выполнении методов indexOf(String s) и indexOf(int ch) .

 

Для метода indexOf(int ch) :

 

public int indexOf(int ch) {

        return indexOf(ch, 0);

    }

 

public int indexOf(int ch, int fromIndex) {

        int max = offset + count;

        char v[] = value;

 

        if (fromIndex < 0) {

            fromIndex = 0;

        } else if (fromIndex >= count) {

            // Note: fromIndex might be near -1>>>1.

            return -1;

        }

 

        int i = offset + fromIndex;

        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {

            // handle most cases here (ch is a BMP code point or a

            // negative value (invalid code point))

            for (; i < max ; i++) {

                if (v[i] == ch) {

                    return i - offset;

                }

            }

            return -1;

        }

 

        if (ch <= Character.MAX_CODE_POINT) {

            // handle supplementary characters here

            char[] surrogates = Character.toChars(ch);

            for (; i < max; i++) {

                if (v[i] == surrogates[0]) {

                    if (i + 1 == max) {

                        break;

                    }

                    if (v[i+1] == surrogates[1]) {

                        return i - offset;

                    }

                }

            }

        }

        return -1;

    }

 

Для метода indexOf(String s) :

 

public int indexOf(String str) {

        return indexOf(str, 0);

    }

 

public int indexOf(String str, int fromIndex) {

        return indexOf(value, offset, count,

                       str.value, str.offset, str.count, fromIndex);

    }

static int indexOf(char[] source, int sourceOffset, int sourceCount,

                       char[] target, int targetOffset, int targetCount,

                       int fromIndex) {

        if (fromIndex >= sourceCount) {

            return (targetCount == 0 ? sourceCount : -1);

        }

            if (fromIndex < 0) {

                fromIndex = 0;

            }

        if (targetCount == 0) {

            return fromIndex;

        }

 

        char first  = target[targetOffset];

        int max = sourceOffset + (sourceCount - targetCount);

 

        for (int i = sourceOffset + fromIndex; i <= max; i++) {

            /* Look for first character. */

            if (source[i] != first) {

                while (++i <= max && source[i] != first);

            }

 

            /* Found first character, now look at the rest of v2 */

            if (i <= max) {

                int j = i + 1;

                int end = j + targetCount - 1;

                for (int k = targetOffset + 1; j < end && source[j] ==

                         target[k]; j++, k++);

 

                if (j == end) {

                    /* Found whole string. */

                    return i - sourceOffset;

                }

            }

        }

        return -1;

    }

 

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

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

 

Для метода indexOf(int ch) :

 

for (; i < max ; i++)

{

 if (v[i] == ch)

 {

  return i - offset;

 }

}

 

Для метода indexOf(String s) :

 

while (++i <= max && source[i] != first);

 

Как видно, для варианта indexOf(int ch) используется цикл for c дополнительной проверкой if, в то время как метод indexOf(String s) обходится одним циклом while c проверкой, вложенной внутри цикла. Вполне очевидно, что приведенные методы рассчитаны на поиск с учетом смещений и для нашего примера несколько избыточны. Выделим и трансформируем необходимый код в отдельный тест.

 

Файл SingleSymbolSearch2.java

/**

 *  @author <a href="mailto:zagrebin_v@mail.ru"> Victor Zagrebin </a>.

 */

class SingleSymbolSearch2

{

 public static void main(String args[])

 {

 

  StringBuffer sb = new StringBuffer("CBABA");

 

  for(int i=0; i<10000000; i++)

  {

   sb.append("AAAAAABCADAAB");

  }

  sb.append("AAAAAAF");

  String word = "AAAAAAF";

  char[] s = sb.toString().toCharArray();

  System.out.println("Characters number in source string="+s.length);

  long start = System.currentTimeMillis();

  int i = 0;

  int pos = 0;

  char first  = 'F';

  int max = s.length;

  while (++i < max && s[i] != first);

  pos = i;

  long end = System.currentTimeMillis();

  System.out.println("indexOf method with String character."+

                     " Elapsed time: " + (end-start)+" msec");

  System.out.println("indexOf method with String character."+

                     " Found string at position: "+pos);

  start = System.currentTimeMillis();

  i=0;

  pos = 0;

  for (; i < max ; i++)

  {

   if (s[i] == first)

   {

    pos = i;

    break;

   }

  }

  end = System.currentTimeMillis();

  System.out.println("indexOf method with character (Unicode code point)."+

" Elapsed time: " + (end-start)+" msec");

  System.out.println("indexOf method with character (Unicode code point)."+

" Found string at position: "+pos);

 }

}

 

Компиляция:

 

javac SingleSymbolSearch.java

 

Запуск:

 

java Xmx1024M SingleSymbolSearch

 

Для запуска требуется наличие достаточного места в оперативной памяти, если выводится ошибка нехватки памяти, то можно уменьшить в строке for(int i=0; i<10000000; i++) максимальное значение индекса в несколько раз.

 

Результаты вывода программы:

 

Characters number in source string=130000012

indexOf method with String character. Elapsed time: 407 msec

indexOf method with String character. Found string at position: 130000011

indexOf method with character (Unicode code point). Elapsed time: 469 msec

indexOf method with character (Unicode code point). Found string at position: 130000011

 

По результатам можно сделать вывод о том, что причина быстродействия метода заключается в использовании компактного цикла с меньшим числом операторов. Поэтому в случае поиска одиночного символа рекомендуется использовать метод indexOf(String s) вместо indexOf(int ch).

Данный вывод распространяется на текущие используемые версии JDK (проверка проводилась для JDK 1.5, 1.6) и для других версий требует тщательной проверки.

 

Ссылки:

 

1. Class java.lang.String. JavaTM 2 Platform Standard Edition 6.0 API Specification.

http://java.sun.com

2. Source code of java.lang.String class.

http://java.sun.com

3. Fast String.indexOf() blog.

http://tomcopeland.blogs.com

 

 

 

 

Автор:

 

 

Загребин Виктор Александрович

 

 

< Раздел “Язык Java > < Раздел “Технологии Java > < Основная страница >

 

Hosted by uCoz