web-dev-qa-db-ja.com

Javaで区切られた文字列を分割する最も速い方法

区切られた文字列で複数列のソート機能を提供するコンパレータを構築しています。私は現在、Stringクラスのsplitメソッドを、生のStringをトークンに分割するための推奨選択肢として使用しています。

これは、生の文字列を文字列配列に変換するための最良の方法ですか?数百万行を並べ替えるので、アプローチが重要だと思います。

正常に動作するようで非常に簡単ですが、Javaでより高速な方法があるかどうかは不明です。

これが私のコンパレータでのソートの仕組みです:

public int compare(String a, String b) {

    String[] aValues = a.split(_delimiter, _columnComparators.length);
    String[] bValues = b.split(_delimiter, _columnComparators.length);
    int result = 0;

    for( int index : _sortColumnIndices ) {
        result = _columnComparators[index].compare(aValues[index], bValues[index]);
        if(result != 0){
            break;
        }
    }
    return result;
}

さまざまなアプローチのベンチマークを行った後、信じられないかもしれませんが、splitメソッドはJavaの最新バージョンを使用した場合に最も迅速でした。ここに私の完成したコンパレータをダウンロードできます: https://sourceforge.net/projects/multicolumnrowcomparator/

10
Constantin

私はこのための迅速で汚れたベンチマークテストを作成しました。 7つの異なる方法を比較します。そのうちのいくつかは、分割されるデータの特定の知識を必要とします。

基本的な汎用分割では、Guava SplitterはString#split()より3.5倍高速であり、それを使用することをお勧めします。 Stringtokenizerはそれよりもわずかに高速で、indexOfを使用した分割は、2倍高速です。

コードと詳細については http://demeranville.com/battle-of-the-tokenizers-delimited-text-parser-performance/ を参照してください

19
tom

@Tomが書いているように、indexOfタイプのアプローチはString.split()よりも高速です。後者は正規表現を処理し、それらのオーバーヘッドが多くなるためです。

ただし、1つのアルゴリズムの変更により、速度が大幅に向上する場合があります。このコンパレータが〜100,000文字列のソートに使用されると想定している場合は、Comparator<String>。なぜなら、ソートの過程で、同じ文字列が比較される可能性が高いためmultiple回なので、分割しますmultiple回など.

すべての文字列onceをString [] sに分割し、Comparator<String[]> St​​ring []をソートします。その後、最後にそれらをすべて組み合わせることができます。

または、マップを使用してString-> String []をキャッシュすることも、その逆も可能です。例えば(スケッチ)また、速度とメモリを交換していることに注意してください。たくさんのRAMがあることを願っています

HashMap<String, String[]> cache = new HashMap();

int compare(String s1, String s2) {
   String[] cached1 = cache.get(s1);
   if (cached1  == null) {
      cached1 = mySuperSplitter(s1):
      cache.put(s1, cached1);
   }
   String[] cached2 = cache.get(s2);
   if (cached2  == null) {
      cached2 = mySuperSplitter(s2):
      cache.put(s2, cached2);
   }

   return compareAsArrays(cached1, cached2);  // real comparison done here
}
5
user949300

このベンチマーク によると、StringTokenizerは文字列を分割する方が高速ですが、配列を返さないため、あまり便利ではありません。

数百万行をソートする必要がある場合は、RDBMSを使用することをお勧めします。

2

これは、大きな(1GB +)タブ区切りファイルの解析に使用する方法です。オーバーヘッドはString.split()よりはるかに少なくなりますが、区切り文字としてcharに制限されます。誰かがより速い方法を持っているなら、私はそれを見たいです。これはCharSequenceおよび_CharSequence.subSequence_でも実行できますが、CharSequence.indexOf(char)の実装が必要です(パッケージメソッドを参照 String.indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) 興味がある場合)。

_public static String[] split(final String line, final char delimiter)
{
    CharSequence[] temp = new CharSequence[(line.length() / 2) + 1];
    int wordCount = 0;
    int i = 0;
    int j = line.indexOf(delimiter, 0); // first substring

    while (j >= 0)
    {
        temp[wordCount++] = line.substring(i, j);
        i = j + 1;
        j = line.indexOf(delimiter, i); // rest of substrings
    }

    temp[wordCount++] = line.substring(i); // last substring

    String[] result = new String[wordCount];
    System.arraycopy(temp, 0, result, 0, wordCount);

    return result;
}
_
1
vallismortis