配列を使用する
論理データを配列に整理する2つの合理的な方法があります。
1つ目は、true / false値を保存することです。
[false, true, true, false, false, true, true]
2番目は、0と1の配列を格納することです。
[0, 1, 1, 0, 0, 1, 1]
どの方法を選択した場合でも、どのような場合でも、そのような配列を文字列に変換し、データを読み取るときにこの文字列を配列に変換する必要があります。 これを行うには、古いメソッド
Array#join()
(または
Array#toString()
)および
String#split()
を使用するか、よりエレガントな
JSON.stringify()
および
JSON.parse()
ます。
JSONパスをたどると、コードは少し短くなりますが、この場合JSONを使用することは、チェーンソーでパンを切るようなものです。 一部のブラウザでは、このアプローチのパフォーマンスへの影響が顕著になり、古いブラウザのサポートがわずかに悪化します。
配列ベースの文字列を使用する主な欠点は、バイト単位のサイズです。 0と1の配列を保存するオプションを選択した場合、値ごとに2文字を使用する必要があります(正確には、2n-1、最後を除く各値の後に1つの区切り文字)。
[0, 1, 1, 0, 0, 1, 1].toString().length // 13 7
したがって、512個の値の場合、 JavaScriptはUTF-16を使用するため、1023文字または2Kが必要です。
true / false値の配列を保持すると、事態はさらに悪化します。
[false, true, true, false, false, true, true].toString().length // 37 7
これは、値ごとに5〜6文字、512値ごとに2560〜3072文字(5〜6 KB)です。
JSON.stringify()
は、括弧の開始と終了にそれぞれ2つの文字を消費しますが、その利点は、
JSON.parse()
結果として、文字列ではなく元の値型を取得することです。
文字列の使用
文字列を使用すると、区切り文字を使用する必要がないため、文字を保存できます。 たとえば、デジタルアプローチを選択し、文字列「01001101010111」を格納する場合、値ごとに1文字を使用します。これは、配列を使用するアプローチよりもはるかに優れています。
String#split
を使用して、値を配列に入れることができます。
'01001101010111'.split(''); // ['0','1','0','0','1','1','0','1','0','1','0','1','1','1']
また、古いブラウザのサポートを心配する必要がない場合は、
string.charAt(i)
または文字列インデックス
(string[i])
を使用したループを使用して、文字列内の文字を単純に
string.charAt(i)
できます。
ビットフィールドを使用する
前の例を考慮して、バイナリについても考えましたか? ビットフィールドの概念は、他のプログラミング言語では非常に一般的ですが、JavaScriptでは一般的ではありません。 一言で言えば、ビットフィールドは、複数の論理値を数値の論理表現のビットにパックするために使用されます。 たとえば、8つの値(true、false、false、true、false、true、true、false)があります。 バイナリでは、これは10010110、10進数で150、16進数で96になります。 したがって、8の代わりに2文字が使用され、 75%が節約されます。 16進表記の単一の数値は、4ビットに正確に対応します。 (16 = 2であるため4。基数が2 nの数値システムでは、各数値にnビットをパックできます。)
したがって、文字列全体を保存し、値ごとに1文字を使用する代わりに、よりスマートな方法でそのような文字列を16進数に変換できます。 どうやってやるの? このように:
parseInt('10010110', 2).toString(16); // '96'
読み取り可能な形式でデータを返す方法は? 簡単な場所はありません:
parseInt('96', 16).toString(2); // '10010110'
これで、前回と同様に、値をループ処理し、ループを使用してそれらの値に対して何か便利なことができます。
さらに改善することは可能ですか?
実際に可能です! 26から6文字のラテンアルファベットのみを使用する16進数システムに変換する理由
Number#toString()
メソッドを使用すると、 ベース36を使用できます。 ベース36の番号システム (
>= 37
場合、
RangeError
取得され
RangeError
)。 したがって、32個の値を6文字に圧縮できます。これは、単純な文字列方式と比較して81.25%の節約になります。 コードはまだシンプルです:
parseInt( '1001011000', 2).toString(36); // 'go' ( '258' ) parseInt('go', 36).toString(2); // '1001011000'
多くはそこで止まります。 しかし、もっと好奇心が強い人は、「大文字やその他の記号はまだありますが、潜在能力をフルに活用していません!」と言います。そして、それらは正しいでしょう。 バイナリファイルをテキストエディタで開くと、画面に奇妙な文字が表示され、数字と文字(大文字と小文字)が混在することは偶然ではありません。 UTF-16でエンコードされた各文字には2バイト(16ビット)が必要です。つまり、適切な圧縮アルゴリズムを使用すると、93.75%の節約になります。
問題は、JavaScriptにはそのようなアルゴリズムを使用するための組み込み機能がないため、コードがもう少し複雑になることです。
1文字に16個の値をパック
String.fromCharCode
メソッドが必要です。 65535までの数値を取り、文字を返します(65535を超える値の場合、空の文字列を返します)。
行をそれぞれ16文字のフラグメントに分割します。
.match(/.{1,16}/g)
これを行うことができます 。 すべてのコードは次のようになります。
function pack(/* string */ values) { var chunks = values.match(/.{1,16}/g), packed = ''; for (var i=0; i < chunks.length; i++) { packed += String.fromCharCode(parseInt(chunks[i], 2)); } return packed; } function unpack(/* string */ packed) { var values = ''; for (var i=0; i < packed.length; i++) { values += packed.charCodeAt(i).toString(2); } return values; }
それほど複雑ではありませんか?
これらの数行のコードにより、上記の512の値を(ドラムロール) 32文字(64バイト)にパックできます! 元の2KB(単純な配列のストレージを使用)よりもはるかに優れていますか?
制限事項
JavaScriptの数値には制限があります。 数値への中間変換を含むここで説明するメソッドの場合、
parseInt('1111…1111', 2)
はさらに多くの
Infinity
を返すため、制限は1023ブール値に設定され
parseInt('1111…1111', 2)
。 この制限は、文字列全体ではなくビットブロックを変換するため、後者の方法には適用されません。 そしてもちろん、この制限はパッキング値をまったく含まないため、最初の2つのメソッド(配列と文字列)には適用されません。
「行き過ぎたと思う」
はい、場合によっては、このような最適化は不要です。 ただし、文字列のみをサポートする限られた場所に一連の論理値を保存する必要がある場合、これらのメソッドは非常に便利です。 また、ワイヤを介して大きな周波数で送信されるものの最適化は決して不要ではありません。 たとえば、Cookieはほとんどすべてのリクエストで送信されるため、可能な限り小さくする必要があります。 別の例は、サーバーの反応が非常に速いはずのオンラインマルチプレイヤーゲームです。そうしないと、このようなゲームをプレイするのはまったく面白くないでしょう。
そして、このような深い最適化があなたのためではないとしても、この記事があなたに考えの糧を与え、おそらく何かを教えてくれたことを願っています。
翻訳 オリジナル(en): JavaScriptを使用したYes / No値の長いリストの最適化
原作者:Lea Verou