web-dev-qa-db-ja.com

JavaのStringクラスがより効率的なindexOf()を実装しないのはなぜですか?

スタックオーバーフローに関する以下の質問の後に

https://stackoverflow.com/questions/5564610/fast-alernative-for-stringindexofstring-str

Java(少なくとも6つ)より効率的な実装を使用しないのはなぜですか?)

コードは次のとおりです。

Java.lang.String#indexOf(String str)

1762    static int indexOf(char[] source, int sourceOffset, int sourceCount,
1763                       char[] target, int targetOffset, int targetCount,
1764                       int fromIndex) {
1765        if (fromIndex >= sourceCount) {
1766            return (targetCount == 0 ? sourceCount : -1);
1767        }
1768        if (fromIndex < 0) {
1769            fromIndex = 0;
1770        }
1771        if (targetCount == 0) {
1772            return fromIndex;
1773        }
1774
1775        char first  = target[targetOffset];
1776        int max = sourceOffset + (sourceCount - targetCount);
1777
1778        for (int i = sourceOffset + fromIndex; i <= max; i++) {
1779            /* Look for first character. */
1780            if (source[i] != first) {
1781                while (++i <= max && source[i] != first);
1782            }
1783
1784            /* Found first character, now look at the rest of v2 */
1785            if (i <= max) {
1786                int j = i + 1;
1787                int end = j + targetCount - 1;
1788                for (int k = targetOffset + 1; j < end && source[j] ==
1789                         target[k]; j++, k++);
1790
1791                if (j == end) {
1792                    /* Found whole string. */
1793                    return i - sourceOffset;
1794                }
1795            }
1796        }
1797        return -1;
1798    }
8
Yaneeve

「効率」はすべてトレードオフに関するものであり、「最良の」アルゴリズムは多くの要因に依存します。 indexOf()の場合、これらの要因の1つは、予想される文字列のサイズです。

JDKのアルゴリズムは、既存の文字配列への単純なインデックス付き参照に基づいています。参照するKnuth-Morris-Prattは、入力文字列と同じサイズの新しいint[]を作成する必要があります。 Boyer-Moore の場合、いくつかの外部テーブルが必要ですが、そのうちの少なくとも1つは2次元です(私が思うに、B-Mを実装したことがありません)。

したがって、問題は次のようになります。追加のオブジェクトの割り当てとルックアップテーブルの作成は、アルゴリズムのパフォーマンスの向上によって相殺されていますか?覚えておいてください、私たちはO(N2)をO(N)に変更しますが、Nごとに実行するステップ数を減らすだけです。

そして、JDKデザイナーが「X文字未満の文字列の場合、単純なアプローチの方が速く、それより長い文字列の通常の使用は期待できません。長い文字列を使用する人は、最適化の方法を知っているでしょう。彼らの検索。」

25
kdgregory

誰もが知っている標準的な効率的な文字列検索アルゴリズムは Boyer-Moore です。特に、文字セットと同じサイズの transition table を作成する必要があります。 ASCIIの場合、これは256エントリの配列です。これは、長い文字列に見合う一定のオーバーヘッドであり、小さな文字列をだれでも気にするほど遅くすることはありません。しかし、Javaは、テーブルのサイズを64Kにする2バイト文字を使用します。通常の使用では、このオーバーヘッドはBoyer-Mooreからの予想されるスピードアップを超えるため、Boyer-Mooreは価値がありません。

もちろん、そのテーブルのほとんどは同じエントリを持つので、例外を効率的な方法で格納し、例外にないものにはデフォルトを提供できると考えるかもしれません。残念ながら、これを行う方法にはルックアップのオーバーヘッドが伴い、コストがかかりすぎて効率的ではありません。 (1つの問題として、予期しない分岐が発生した場合に パイプラインストール が発生し、コストが高くなる傾向があることに注意してください。)

Unicodeでは、この問題はエンコーディングに大きく依存することに注意してください。 Javaが作成された場合、Unicodeは64 K以内に収まるため、Javaは1文字あたり2バイトを使用し、文字列の長さは単に分割されたバイト数でした。 (このエンコーディングはUCS-2と呼ばれていました。)これにより、特定の文字にジャンプしたり、特定の部分文字列を抽出したりするのが速くなり、indexOf()の非効率性は問題になりませんでした。成長したので、Unicode文字はJava文字に必ずしも適合しません。これは、Javaが彼らが回避しようとしていたサイズの問題に陥りました。(そのエンコーディング現在はUTF-16です。)下位互換性のために、Java文字のサイズを変更することはできませんでしたが、Unicode文字とJava =文字は同じものです。違いはありますが、Javaプログラマーはそれを知っており、日常生活で遭遇する可能性はさらに低いです(Windowsと.NETは同じように同じ理由でパス)

他の一部の言語および環境では、代わりにUTF-8が使用されます。 ASCIIは有効なUnicodeであり、Boyer-Mooreが効率的です。トレードオフは、可変バイトの問題に注意を怠ると、実際よりもはるかに明確にヒットすることです。 UTF-16。

11
btilly

それは主にこれに帰着します:最も明白な改善はボイヤー・ムーア、またはそのいくつかの変種によるものです。ただし、B-Mとバリアントは完全に異なるインターフェースを必要としています。

特に、Boyer-Mooreと導関数は実際には2つのステップで機能します。最初に初期化を行います。これは、検索する文字列forにのみ基づいてテーブルを作成します。これにより、必要に応じて何度でもその文字列を検索するために使用できるテーブルが作成されます。

あなたは確かにcouldテーブルをメモし、同じターゲット文字列の後続の検索にそれを使用することにより、これを既存のインターフェースに適合させます。これは、Sunの本来の目的であるこの機能にはあまり適合しないと思います。それは、他のほとんどに依存しない低レベルのビルディングブロックであることです。それをかなり他のインフラストラクチャに依存する高レベルの関数にすることは、(とりわけ)使用したメモ化インフラストラクチャが部分文字列検索を使用できないようにする必要があることを意味します。

その最も可能性の高い結果は、このようなもの(つまり、スタンドアロンの検索ルーチン)を別の名前で単純に再実装し、より高いレベルのルーチンを既存の名前で再実装することだと思います。すべてのことを考慮すると、新しい名前で新しい高レベルのルーチンを作成するほうがおそらく意味があると思います。

それに対する明白な代替策は、ある種の簡略化されたバージョンのメモ化を使用することです。たとえば、静的に1つのテーブルのみを保存し、ターゲット文字列がテーブルの作成に使用されたものと同一である場合にそれを再利用します。 。それは確かに可能ですが、多くのユースケースでは最適とは言えません。スレッドセーフにすることも簡単ではありません。

別の可能性は、B-M検索の2ステップの性質を明示的に公開することです。私は誰もが本当にそのアイデアを気に入っているとは思いません-それはかなり高いコスト(不器用さ、親しみやすさの欠如)を伴い、多くのユースケースにはほとんどまたはまったくメリットがありません(この件に関するほとんどの研究は、平均文字列長が20文字)。

1
Jerry Coffin