手書きの最も単純なLRUアルゴリズム

手書きの最も単純なLRUアルゴリズム

1 LRUとは何か

LRU (Least Recently Used) は、最も最近使用されていないデータです。その基本的な考え方は、「データが最近アクセスされた場合、将来アクセスされる可能性が高くなる」というものです。したがって、LRU アルゴリズムは、過去のアクセス レコードに従ってデータを並べ替えます。十分なスペースがない場合は、最も最近使用されていないデータが削除されます。

2 LRU実装の原則

LRU アルゴリズムは最近使用されたデータを優先するため、ソートをサポートするデータ構造が必要であり、リンク リストが非常に適しています。

配列を検討してみませんか?

LRU アルゴリズムは一般的にアクセス頻度の高いシナリオで使用されるため、データの移動は頻繁に行われます。配列を移動したら、移動した値の後ろにあるすべてのデータの位置を変更する必要があります。これは非効率的であり、推奨されません。

3. 双方向リンクリストのLinkedHashMap

先ほど、LRU アルゴリズムの実装はリンク リストを使用して実装できることを分析しました。Java の LinkedHashMap は双方向のリンク リストです。

LinkedHashMap は HashMap のサブクラスです。HashMap データ構造に基づいて、すべてのエントリをリンクする双方向リンク リストも維持します。このリンク リストは反復順序を定義します。これは通常、データが挿入される順序です。

LinkedHashMap のソースコードを見てみましょう。

ソース コードの定義から、accessOrder プロパティで LinkedHashMap をトラバースする順序を指定できることがわかります。true はアクセス順序、false は挿入順序を意味し、デフォルトは false です。

LRU はアクセス順序に敏感なので、単純に検証するために true を使用します。

  1. パブリッククラスLRUTest {
  2. 公共 静的void main(String[] args) {
  3. LinkedHashMap<String, Object> マップ = new LinkedHashMap<>(16, 0.75f, true );
  4. map.put( "a" 、1);
  5. map.put( "b" 、2);
  6. map.put( "c" 、3);
  7. System.out.println ( "get の前に " + map) ;
  8. マップ取得します
  9. System.out.println ( "get 後" + map) ;
  10. }}

結果は次のとおりです。

  1. 前に {a=1, b=2, c=3} を取得します
  2. 取得{b=2, c=3, a=1}

accessOrder = true を設定すると、LinkedHashMap をアクセス順にソートできることがわかります。

では、LinkedHashMap はどのようにそれを実現するのでしょうか?

getメソッドを見てみましょう

  1. パブリックV get(オブジェクトキー) {
  2. ノード<K,V> e;
  3. // ノードを取得する
  4. ((e = getNode(hash( key ), key )) == null の場合)
  5. 戻る ヌル;
  6. // accessOrder = trueの場合、afterNodeAccess メソッドを実行します
  7. if (アクセス順序)
  8. afterNodeAccess(e);
  9. e.valueを返します
  10. }

afterNodeAccess メソッドをもう一度見てみると、ノードが移動されていることがわかります。ここまでで、ノードを移動する原理は理解できました。

  1. void afterNodeAccess(Node<K,V> e) { //ノード移動する 最後 
  2. LinkedHashMap.Entry<K,V>最後;
  3. if (accessOrder && ( last = tail ) != e) {
  4. LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e、b = p.before、a = p.after ; p.after = null ; if (b == null )
  5. ヘッド = a;それ以外 
  6. b. after = a; if (a != null )
  7. a.before = b;そうでなければ 
  8. 最後= b;
  9. 最後== null の場合
  10. ヘッド = p;それ以外の場合{
  11. p.before =最後;
  12. 最後. after = p; } 末尾 = p; ++modCount; }}

現在、LinkedHashMap を LRU として使用する場合、容量が限られている場合に古いデータをどのように削除するかという、まだ気になる問題があります。

戻ってputメソッドを見てみましょう

  1. 公開V put(Kキー、V値) {
  2. putVal(hash( key ), key , value, false , true )を返します
  3. }
  4. 最終的な V putVal( intハッシュ、 Kキー、 V 値、 boolean onlyIfAbsent、 boolean evict) {
  5. Node<K,V>[] tab; Node<K,V> p; int n, i;
  6. ((tab = table ) == null || (n = tab.length) == 0)の場合
  7. n = (タブ = resize()).length;
  8. ((p = tab[i = (n - 1) & hash] ) == null の場合
  9. tab[i] = newNode(ハッシュ、キー、値、 null );
  10. それ以外{
  11. ノード<K,V> e; K k;
  12. if (p.hash == ハッシュ &&
  13. ((k = p.key ) == key || ( key != null && key .equals(k))))
  14. e = p;
  15. そうでない場合 (p TreeNode のインスタンス)
  16. e = ((TreeNode<K,V>)p).putTreeVal(this、タブ、ハッシュ、キー、値);
  17. それ以外{
  18. ( int binCount = 0; ; ++binCount) {
  19. ((e = p.next ) == null の場合) {
  20. p.next = newNode(ハッシュ、キー、値、 null );
  21. if (binCount >= TREEIFY_THRESHOLD - 1) // 1番目-1
  22. treeifyBin(タブ、ハッシュ);
  23. 壊す;
  24. }
  25. if (e.hash == hash &&
  26. ((k = e.key ) == key || ( key != null && key .equals(k))))
  27. 壊す;
  28. p = e;
  29. }
  30. }
  31. if (e != null ) { // 既存のマッピング  
  32. V 古い値 = e.value;
  33. if (!onlyIfAbsent || oldValue == null )
  34. e.value = 値;
  35. afterNodeAccess(e);
  36. 古い値を返します
  37. }
  38. }
  39. ++modCount;
  40. if (++サイズ> しきい値)
  41. サイズを変更します。
  42. afterNodeInsertion(削除);
  43. 戻る ヌル;
  44. }
  45. void afterNodeInsertion(boolean evict) { // 最長のものを削除する可能性がある
  46. LinkedHashMap.Entry<K,V>を最初に;
  47. if (evict && ( first = head) != null && removeEldestEntry( first )) {
  48. Kキー=最初の.キー;
  49. ノードを削除します(ハッシュ(キー),キー, null , false , true );
  50. }
  51. }

put メソッドをステップごとに見ていくと、removeEldestEntry(first) メソッドが true を返すとヘッドが削除され、最近使用されていないデータが排除されることがわかります。完全に LRU に準拠しています。

4 最も単純なLRU実装

上記の分析に基づいて、最も単純なLRUを次のように実装できます。

  1. パブリッククラスLRUCache<K,V>はLinkedHashMap<K,V>を拡張します。
  2. プライベートintキャッシュサイズ;
  3. パブリックLRUCache( intキャッシュサイズ){
  4. // 注意: ここでは accessOrder = trueが必要です 
  5. super(キャッシュサイズ、0.75f、 true );
  6. this.cacheSize = キャッシュサイズ;
  7. }
  8. /**
  9. * 要素数がキャッシュ容量を超えているかどうかを判断し、超えている場合は削除します。
  10. */
  11. @オーバーライド
  12. 保護されたブール値の長男エントリを削除します(Map.Entry<K, V> 長男) {
  13. 戻る サイズ() > キャッシュサイズ;
  14. }
  15. }

<<:  米国のAI雇用市場の現在の規模を解読する

>>:  COVID-19パンデミックは不動産業界のインテリジェントな変革とアップグレードを加速させた

ブログ    
ブログ    

推薦する

スマートインフラがコミュニティを良くする5つの方法

フロスト&サリバンによる最近の分析によると、スマートシティ技術への世界的な投資は2025年までに22...

AI+サイエンス: PaddlePaddle をベースにした AlphaFold2 でタンパク質構造予測を実現

1958 年、FHC クリックは、生物学における重要なセントラルドグマである DNA -> R...

産業用 AI チェックリスト: 始めるための 10 ステップ

人類はもはや人工知能(AI)の波から逃れることはできない。彼らが行くところすべてで、最新の AI ソ...

「激怒」するビッグモデルがレコメンデーションシステムと衝突したとき

ChatGPTに代表される大規模モデル技術の急速な発展により、レコメンデーションシステムは革命的な変...

スマートフォンの代替品?元アップルデザイナーが699ドルの人工知能ブローチ「AI Pin」を発売

海外メディアの報道によると、元アップルのデザイナー、イムラン・チャウドリ氏とベサニー・ボンジョルノ氏...

将来の顔認識技術の最大の問題は、それがほぼ間違いのないものであることだ

近年、顔認識技術は、女性や有色人種の誤検出率の高さや、個人の自由やプライバシーへの悪影響など、常に世...

2つのセッションは「AI顔認識」と生体認証データの法制化と規制の緊急の必要性に焦点を当てています。

[[385416]]現在、両セッションは活発に行われており、全国のさまざまな分野の代表者が独自の提...

マイクロソフトは2022年にリモートワーカー向けに3Dワークスペースを提供する予定

Microsoft は、仮想会議用に Mesh for Teams と呼ばれる没入型 3D プラット...

1 週間で機械学習を始めることは信頼できるでしょうか?詳しい学習スケジュールはこちら

[[185648]]原著者 | ペル・ハラルド・ボルゲン編集:魏子民、頼暁娟、張立軍 「初心者にとっ...

人工知能のように製品にユーザーを理解させるにはどうすればよいでしょうか?これらの方法をまとめてみました!

ほとんどの人は、ロボットやアプリケーション ツールについて話すときにインテリジェンスについて言及しま...

人工知能搭載の携帯電話は私たちの生活をどのように変えるのでしょうか? 携帯電話メーカーが何をしてきたか見てみましょう。

チャットができる「インテリジェント音声アシスタント」から、さまざまな家電を操作できるスマートスピーカ...

ファイアウォールは再び進化します。よりスマートで安全になりましたか?

ハッカーがネットワーク攻撃を開始すると、まず会社のパブリック IP で SSH サービスに使用される...

...

ジャック・マー:世界の未来を決めるのは技術ではなく、技術の背後にある人々、理想、価値観だ

5月16日、天津梅江会議展示センターで第2回世界知能大会が開幕した。アリババグループ取締役会長のジャ...

...