আপনি জাভাতে কীভাবে একটি এলআরইউ ক্যাশে প্রয়োগ করবেন?


169

দয়া করে এএইচসি বা ওএসচি ইত্যাদি বলবেন না এই প্রশ্নের উদ্দেশ্যে ধরে নিই যে আমি কেবল নিজের এসডিকে (করে শিখিয়ে) ব্যবহার করে নিজের প্রয়োগ করতে চাই। ক্যাশেটি বহুবিবাহিত পরিবেশে ব্যবহৃত হবে তা আপনি কোন ডেটাস্ট্রাকচার ব্যবহার করবেন? আমি ইতিমধ্যে লিংকডহ্যাশম্যাপ এবং সংগ্রহগুলি # সিঙ্ক্রোনাইজড ম্যাপ ব্যবহার করে একটি বাস্তবায়ন করেছি , তবে নতুন উত্সাহী সংগ্রহগুলির মধ্যে আরও ভাল প্রার্থী হলে আমি আগ্রহী।

আপডেট: আমি কেবল ইয়াগের সর্বশেষটি পড়ছিলাম যখন আমি এই নাগেটটি পেয়েছি:

আপনার যদি ধ্রুবক অ্যাক্সেসের প্রয়োজন হয় এবং সন্নিবেশ ক্রম বজায় রাখতে চান তবে আপনি একটি লিঙ্কডহ্যাশম্যাপের চেয়ে আরও ভাল করতে পারবেন না, সত্যই একটি দুর্দান্ত ডেটা কাঠামো। যদি সামনের সংস্করণ থাকে তবে এটি সম্ভবত আরও দুর্দান্ত হতে পারে way কিন্তু হায়.

আমি উপরে উল্লিখিত LinkedHashMap+ Collections#synchronizedMapবাস্তবায়নের সাথে যাওয়ার আগে আমি প্রায় একই জিনিসটি ভাবছিলাম । জেনে ভালো লাগল আমি সবেমাত্র কিছু উপেক্ষা করিনি।

এখনও অবধি উত্তরগুলির উপর ভিত্তি করে, এটি মনে হচ্ছে অত্যন্ত সামনের এলআরইউয়ের জন্য আমার সেরা বাজিটি একই রকম যুক্তি ব্যবহার করে যা কিছু ব্যবহার করে সমকালীন হ্যাশম্যাপটি প্রসারিত করা হবে LinkedHashMap



উত্তর:


102

আমি এই পরামর্শগুলি প্রচুর পছন্দ করি তবে আপাতত আমি মনে করি আমি LinkedHashMap+ বদ্ধ থাকি Collections.synchronizedMap। ভবিষ্যতে যদি আমি এটি আবার ঘুরে দেখি তবে আমি সম্ভবত ConcurrentHashMapএকইভাবে LinkedHashMapপ্রসারিত করার জন্য কাজ করব HashMap

হালনাগাদ:

অনুরোধ করে, আমার বর্তমান বাস্তবায়নের সূচনাটি এখানে।

private class LruCache<A, B> extends LinkedHashMap<A, B> {
    private final int maxEntries;

    public LruCache(final int maxEntries) {
        super(maxEntries + 1, 1.0f, true);
        this.maxEntries = maxEntries;
    }

    /**
     * Returns <tt>true</tt> if this <code>LruCache</code> has more entries than the maximum specified when it was
     * created.
     *
     * <p>
     * This method <em>does not</em> modify the underlying <code>Map</code>; it relies on the implementation of
     * <code>LinkedHashMap</code> to do that, but that behavior is documented in the JavaDoc for
     * <code>LinkedHashMap</code>.
     * </p>
     *
     * @param eldest
     *            the <code>Entry</code> in question; this implementation doesn't care what it is, since the
     *            implementation is only dependent on the size of the cache
     * @return <tt>true</tt> if the oldest
     * @see java.util.LinkedHashMap#removeEldestEntry(Map.Entry)
     */
    @Override
    protected boolean removeEldestEntry(final Map.Entry<A, B> eldest) {
        return super.size() > maxEntries;
    }
}

Map<String, String> example = Collections.synchronizedMap(new LruCache<String, String>(CACHE_SIZE));

15
আমি তবে এখানে উত্তরাধিকারের পরিবর্তে এনক্যাপসুলেশন ব্যবহার করতে চাই। এটি কার্যকর জাভা থেকে আমি শিখেছি।
কপিল ডি

10
@ ক্যাপিলডি কিছুক্ষণ হয়ে গেছে, তবে আমি LinkedHashMapএলআরইউ বাস্তবায়ন তৈরির জন্য স্পষ্টভাবে এই পদ্ধতির অনুমোদনের জন্য জাভাডক্স প্রায় ইতিবাচক ।
হ্যাঙ্ক গে

7
@ হ্যাঙ্কগে জাওয়ার লিংকডহ্যাশম্যাপ (তৃতীয় প্যারামিটার সহ = সত্য) কোনও এলআরইউ ক্যাশে নয়। কারণ এন্ট্রি পুনরায় স্থাপন করা এন্ট্রিগুলির ক্রমকে প্রভাবিত করে না (একটি আসল এলআরইউ ক্যাশে
পুনরুক্তি

2
@ পেসারিয়র আমি এই আচরণটি মোটেও দেখছি না। অ্যাক্সেস অর্ডার সক্ষম মানচিত্রে, সমস্ত ক্রিয়া সর্বাধিক ব্যবহৃত (নবীনতম) হিসাবে একটি এন্ট্রি করে: প্রাথমিক সন্নিবেশ, মান আপডেট এবং মান পুনরুদ্ধার। আমি কিছু অনুপস্থিত করছি?
এসাইলিজা

3
@ পেসারিয়ার "একটি এন্ট্রি পুনরায় স্থাপন করা এন্ট্রিগুলির ক্রমকে প্রভাবিত করে না", এটি ভুল। যদি আপনি "পুট" পদ্ধতির জন্য লিংকডহ্যাশম্যাপের প্রয়োগের দিকে নজর দেন তবে এটি হ্যাশম্যাপ থেকে প্রয়োগের উত্তরাধিকার সূত্রে প্রাপ্ত। এবং হ্যাশম্যাপের জাভাদোক বলেছেন "যদি মানচিত্রে আগে কীটির জন্য ম্যাপিং থাকে তবে পুরানো মান প্রতিস্থাপন করা হয়"। এবং যদি আপনি এর উত্স কোডটি পরীক্ষা করে দেখেন, পুরানো মানটিকে প্রতিস্থাপন করার সময়, এটি রেকর্ডঅ্যাক্সেস পদ্ধতিটি কল করবে এবং লিংকডহ্যাশম্যাপের রেকর্ডঅ্যাক্সেস পদ্ধতিতে এটি দেখতে এটির মতো দেখাচ্ছে: যদি (lm.accessOrder) {lm.modCount ++; অপসারণ(); অ্যাডবয়েফর (lm.header);}
nybon


10

এটি গোলাকার দুই।

প্রথম দফায় আমি যা নিয়ে এসেছিলাম তারপরে আমি ডোমেনটি দিয়ে আমার মন্তব্যে কিছুটা আরও আবদ্ধ মন্তব্যগুলি পুনরায় পড়ি।

সুতরাং এখানে ইউনিট পরীক্ষার সহজতম সংস্করণ এটি দেখায় যা এটি অন্যান্য সংস্করণের উপর ভিত্তি করে কাজ করে।

প্রথম অ-সমবর্তী সংস্করণ:

import java.util.LinkedHashMap;
import java.util.Map;

public class LruSimpleCache<K, V> implements LruCache <K, V>{

    Map<K, V> map = new LinkedHashMap (  );


    public LruSimpleCache (final int limit) {
           map = new LinkedHashMap <K, V> (16, 0.75f, true) {
               @Override
               protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
                   return super.size() > limit;
               }
           };
    }
    @Override
    public void put ( K key, V value ) {
        map.put ( key, value );
    }

    @Override
    public V get ( K key ) {
        return map.get(key);
    }

    //For testing only
    @Override
    public V getSilent ( K key ) {
        V value =  map.get ( key );
        if (value!=null) {
            map.remove ( key );
            map.put(key, value);
        }
        return value;
    }

    @Override
    public void remove ( K key ) {
        map.remove ( key );
    }

    @Override
    public int size () {
        return map.size ();
    }

    public String toString() {
        return map.toString ();
    }


}

আসল পতাকাটি গেটস এবং পুটসের অ্যাক্সেসকে ট্র্যাক করবে। জাভাডোকস দেখুন। কনস্ট্রাক্টরের সত্যিকারের পতাকা ছাড়াই সরানোএডলস্ট্রি এন্ট্রি কেবল একটি ফিফো ক্যাশে প্রয়োগ করবে (ফিফোর নীচে নোটগুলি দেখুন এবং রিলিজএলডেস্ট্রি)।

এখানে পরীক্ষাটি প্রমাণ করে যে এটি একটি এলআরইউ ক্যাশে হিসাবে কাজ করে:

public class LruSimpleTest {

    @Test
    public void test () {
        LruCache <Integer, Integer> cache = new LruSimpleCache<> ( 4 );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        boolean ok = cache.size () == 4 || die ( "size" + cache.size () );


        cache.put ( 4, 4 );
        cache.put ( 5, 5 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == 4 || die ();
        ok |= cache.getSilent ( 5 ) == 5 || die ();


        cache.get ( 2 );
        cache.get ( 3 );
        cache.put ( 6, 6 );
        cache.put ( 7, 7 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == null || die ();
        ok |= cache.getSilent ( 5 ) == null || die ();


        if ( !ok ) die ();

    }

এখন সমবর্তী সংস্করণের জন্য ...

org.boon.cache প্যাকেজ;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LruSimpleConcurrentCache<K, V> implements LruCache<K, V> {

    final CacheMap<K, V>[] cacheRegions;


    private static class CacheMap<K, V> extends LinkedHashMap<K, V> {
        private final ReadWriteLock readWriteLock;
        private final int limit;

        CacheMap ( final int limit, boolean fair ) {
            super ( 16, 0.75f, true );
            this.limit = limit;
            readWriteLock = new ReentrantReadWriteLock ( fair );

        }

        protected boolean removeEldestEntry ( final Map.Entry<K, V> eldest ) {
            return super.size () > limit;
        }


        @Override
        public V put ( K key, V value ) {
            readWriteLock.writeLock ().lock ();

            V old;
            try {

                old = super.put ( key, value );
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return old;

        }


        @Override
        public V get ( Object key ) {
            readWriteLock.writeLock ().lock ();
            V value;

            try {

                value = super.get ( key );
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return value;
        }

        @Override
        public V remove ( Object key ) {

            readWriteLock.writeLock ().lock ();
            V value;

            try {

                value = super.remove ( key );
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return value;

        }

        public V getSilent ( K key ) {
            readWriteLock.writeLock ().lock ();

            V value;

            try {

                value = this.get ( key );
                if ( value != null ) {
                    this.remove ( key );
                    this.put ( key, value );
                }
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return value;

        }

        public int size () {
            readWriteLock.readLock ().lock ();
            int size = -1;
            try {
                size = super.size ();
            } finally {
                readWriteLock.readLock ().unlock ();
            }
            return size;
        }

        public String toString () {
            readWriteLock.readLock ().lock ();
            String str;
            try {
                str = super.toString ();
            } finally {
                readWriteLock.readLock ().unlock ();
            }
            return str;
        }


    }

    public LruSimpleConcurrentCache ( final int limit, boolean fair ) {
        int cores = Runtime.getRuntime ().availableProcessors ();
        int stripeSize = cores < 2 ? 4 : cores * 2;
        cacheRegions = new CacheMap[ stripeSize ];
        for ( int index = 0; index < cacheRegions.length; index++ ) {
            cacheRegions[ index ] = new CacheMap<> ( limit / cacheRegions.length, fair );
        }
    }

    public LruSimpleConcurrentCache ( final int concurrency, final int limit, boolean fair ) {

        cacheRegions = new CacheMap[ concurrency ];
        for ( int index = 0; index < cacheRegions.length; index++ ) {
            cacheRegions[ index ] = new CacheMap<> ( limit / cacheRegions.length, fair );
        }
    }

    private int stripeIndex ( K key ) {
        int hashCode = key.hashCode () * 31;
        return hashCode % ( cacheRegions.length );
    }

    private CacheMap<K, V> map ( K key ) {
        return cacheRegions[ stripeIndex ( key ) ];
    }

    @Override
    public void put ( K key, V value ) {

        map ( key ).put ( key, value );
    }

    @Override
    public V get ( K key ) {
        return map ( key ).get ( key );
    }

    //For testing only
    @Override
    public V getSilent ( K key ) {
        return map ( key ).getSilent ( key );

    }

    @Override
    public void remove ( K key ) {
        map ( key ).remove ( key );
    }

    @Override
    public int size () {
        int size = 0;
        for ( CacheMap<K, V> cache : cacheRegions ) {
            size += cache.size ();
        }
        return size;
    }

    public String toString () {

        StringBuilder builder = new StringBuilder ();
        for ( CacheMap<K, V> cache : cacheRegions ) {
            builder.append ( cache.toString () ).append ( '\n' );
        }

        return builder.toString ();
    }


}

আমি দেখতে পাচ্ছি কেন আমি প্রথমে অ-সমবর্তী সংস্করণটি কভার করি। লক কনটেন্ট হ্রাস করার জন্য উপরেরগুলি কিছু স্ট্রিপ তৈরি করার চেষ্টা করে। সুতরাং আমরা এটি কীটি নিয়েছি এবং তারপরে আসল ক্যাশে সন্ধান করার জন্য সেই হ্যাশটি সন্ধান করব। এটি আপনার কী হ্যাশ অ্যালগরিদমকে কতটা ভালভাবে ছড়িয়েছে তার উপর নির্ভর করে ন্যায্য পরিমাণ ত্রুটির মধ্যে সীমাবদ্ধ আকারের প্রস্তাবনা / মোটামুটি অনুমানের আরও বেশি করে তোলে।

একযোগে সংস্করণ সম্ভবত কাজ করে তা দেখানোর জন্য এখানে পরীক্ষা দেওয়া হচ্ছে। :) (আগুনের অধীনে পরীক্ষাটি আসল উপায় হবে)।

public class SimpleConcurrentLRUCache {


    @Test
    public void test () {
        LruCache <Integer, Integer> cache = new LruSimpleConcurrentCache<> ( 1, 4, false );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        boolean ok = cache.size () == 4 || die ( "size" + cache.size () );


        cache.put ( 4, 4 );
        cache.put ( 5, 5 );

        puts (cache);
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == 4 || die ();
        ok |= cache.getSilent ( 5 ) == 5 || die ();


        cache.get ( 2 );
        cache.get ( 3 );
        cache.put ( 6, 6 );
        cache.put ( 7, 7 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();

        cache.put ( 8, 8 );
        cache.put ( 9, 9 );

        ok |= cache.getSilent ( 4 ) == null || die ();
        ok |= cache.getSilent ( 5 ) == null || die ();


        puts (cache);


        if ( !ok ) die ();

    }


    @Test
    public void test2 () {
        LruCache <Integer, Integer> cache = new LruSimpleConcurrentCache<> ( 400, false );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        for (int index =0 ; index < 5_000; index++) {
            cache.get(0);
            cache.get ( 1 );
            cache.put ( 2, index  );
            cache.put ( 3, index );
            cache.put(index, index);
        }

        boolean ok = cache.getSilent ( 0 ) == 0 || die ();
        ok |= cache.getSilent ( 1 ) == 1 || die ();
        ok |= cache.getSilent ( 2 ) != null || die ();
        ok |= cache.getSilent ( 3 ) != null || die ();

        ok |= cache.size () < 600 || die();
        if ( !ok ) die ();



    }

}

এটিই সর্বশেষ পোস্ট .. এটি একটি এলএফইউ হিসাবে এলআরইউ ক্যাশে নয় বলে আমি প্রথম পোস্টটি মুছলাম।

আমি ভেবেছিলাম এটিকে আরেকবার যাব। আমি স্ট্যান্ডার্ড জেডি কে ডাব্লু / ও খুব বেশি প্রয়োগকরণ ব্যবহার করে একটি এলআরইউ ক্যাশের সহজতম সংস্করণ নিয়ে আসার চেষ্টা করছিলাম।

এখানে আমি যা নিয়ে এসেছি তা এখানে। আমি এবং এলআরইউয়ের পরিবর্তে এলএফইউ প্রয়োগ করায় আমার প্রথম প্রয়াস কিছুটা বিপর্যয় হয়েছিল এবং তারপরে আমি এতে ফিফও এবং এলআরইউ সমর্থন যুক্ত করেছি ... এবং তখন আমি বুঝতে পারি এটি একটি দৈত্য হয়ে উঠছে। তারপরে আমি আমার বন্ধু জনের সাথে কথা বলতে শুরু করলাম যিনি সবে আগ্রহী ছিলেন এবং আমি তখন গভীর দৈর্ঘ্যে বর্ণনা করেছিলাম কীভাবে আমি কীভাবে এলএফইউ, এলআরইউ এবং ফিফো বাস্তবায়িত করেছি এবং আপনি কীভাবে এটি একটি সহজ এনইউএম আর্গ দিয়ে স্যুইচ করতে পারবেন এবং আমি বুঝতে পেরেছিলাম যে আমি সত্যিই চেয়েছিলাম একটি সাধারণ এলআরইউ ছিল। সুতরাং আমার কাছ থেকে আগের পোস্টটি উপেক্ষা করুন, এবং আপনি যদি একটি এনআরএমের মাধ্যমে পরিবর্তনযোগ্য কোনও এলআরইউ / এলএফইউ / ফিফো ক্যাশে দেখতে চান তবে আমাকে জানতে দিন ... না? ঠিক আছে .. এখানে তিনি যান।

কেবল জেডিকে ব্যবহার করে সবচেয়ে সহজতম এলআরইউ। আমি একসাথে সংস্করণ এবং একটি সহ-সংস্করণ উভয়ই প্রয়োগ করেছি implemented

আমি একটি সাধারণ ইন্টারফেস তৈরি করেছি (এটি ন্যূনতমতা তাই সম্ভবত আপনার পছন্দ মতো কয়েকটি বৈশিষ্ট্য অনুপস্থিত তবে এটি আমার ব্যবহারের ক্ষেত্রে কাজ করে তবে আপনি যদি XYZ বৈশিষ্ট্যটি দেখতে চান তবে আমাকে জানাতে দিন ... আমি কোড লিখতে বাঁচি)) ।

public interface LruCache<KEY, VALUE> {
    void put ( KEY key, VALUE value );

    VALUE get ( KEY key );

    VALUE getSilent ( KEY key );

    void remove ( KEY key );

    int size ();
}

আপনি অবাক হতে পারেন কি getSilent কি। আমি এটি পরীক্ষার জন্য ব্যবহার করি। getSilent কোনও আইটেমের LRU স্কোর পরিবর্তন করে না।

প্রথমে অসমাপ্ত এক ....

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class LruCacheNormal<KEY, VALUE> implements LruCache<KEY,VALUE> {

    Map<KEY, VALUE> map = new HashMap<> ();
    Deque<KEY> queue = new LinkedList<> ();
    final int limit;


    public LruCacheNormal ( int limit ) {
        this.limit = limit;
    }

    public void put ( KEY key, VALUE value ) {
        VALUE oldValue = map.put ( key, value );

        /*If there was already an object under this key,
         then remove it before adding to queue
         Frequently used keys will be at the top so the search could be fast.
         */
        if ( oldValue != null ) {
            queue.removeFirstOccurrence ( key );
        }
        queue.addFirst ( key );

        if ( map.size () > limit ) {
            final KEY removedKey = queue.removeLast ();
            map.remove ( removedKey );
        }

    }


    public VALUE get ( KEY key ) {

        /* Frequently used keys will be at the top so the search could be fast.*/
        queue.removeFirstOccurrence ( key );
        queue.addFirst ( key );
        return map.get ( key );
    }


    public VALUE getSilent ( KEY key ) {

        return map.get ( key );
    }

    public void remove ( KEY key ) {

        /* Frequently used keys will be at the top so the search could be fast.*/
        queue.removeFirstOccurrence ( key );
        map.remove ( key );
    }

    public int size () {
        return map.size ();
    }

    public String toString() {
        return map.toString ();
    }
}

queue.removeFirstOccurrence যদি আপনি একটি বড় ক্যাশে আছে একটি সম্ভাব্য ব্যয়বহুল অপারেশন। লিংকডলিস্টকে উদাহরণ হিসাবে নেওয়া যেতে পারে এবং অপসারণের কাজগুলি খুব বেশি দ্রুত এবং আরও সামঞ্জস্যপূর্ণ করার জন্য উপাদান থেকে নোডের মধ্যে একটি বিপরীত অনুসন্ধানের হ্যাশ ম্যাপ যুক্ত করতে পারে। আমিও শুরু করেছিলাম, কিন্তু তখন বুঝতে পেরেছিলাম এর দরকার নেই। কিন্তু সম্ভবত...

যখন করা বলা হয়, কী সারিতে যোগ হয়। যখন পাবেন বলা হয়, কী সেটি সরানো এবং কিউ শীর্ষে পুনরায় যোগ করা হয়েছে।

যদি আপনার ক্যাশেটি ছোট হয় এবং কোনও আইটেম তৈরি করা ব্যয়বহুল হয় তবে এটি ভাল ক্যাশে হওয়া উচিত। যদি আপনার ক্যাশেটি সত্যিই বড় হয় তবে লিনিয়ার সন্ধানটি বোতলের ঘা হতে পারে বিশেষত আপনার যদি ক্যাশের গরম অঞ্চল না থাকে। তীব্র গরম দাগগুলি যত তীব্র হবে, গরম আইটেম হিসাবে রৈখিক অনুসন্ধান তত দ্রুত রৈখিক অনুসন্ধানের শীর্ষে থাকে। যাইহোক ... এটি আরও দ্রুত যাওয়ার জন্য যা প্রয়োজন তা হল আরেকটি লিংকডলিস্ট লিখুন যাতে একটি অপসারণ অপারেশন রয়েছে যা অপসারণের জন্য নোড লুকিংয়ের বিপরীত উপাদান রয়েছে, তারপরে অপসারণ হ্যাশ ম্যাপ থেকে কোনও কী মুছে ফেলার মতো দ্রুত হবে।

আপনার যদি 1000 টি আইটেমের নীচে ক্যাশে থাকে তবে এটি ভাল কাজ করবে।

এটির ক্রিয়াকলাপগুলি কার্যকরভাবে দেখানোর জন্য এটি একটি সাধারণ পরীক্ষা।

public class LruCacheTest {

    @Test
    public void test () {
        LruCache<Integer, Integer> cache = new LruCacheNormal<> ( 4 );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        boolean ok = cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 0 ) == 0 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();


        cache.put ( 4, 4 );
        cache.put ( 5, 5 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 0 ) == null || die ();
        ok |= cache.getSilent ( 1 ) == null || die ();
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == 4 || die ();
        ok |= cache.getSilent ( 5 ) == 5 || die ();

        if ( !ok ) die ();

    }
}

শেষ এলআরইউ ক্যাশেটি একক থ্রেডযুক্ত ছিল এবং দয়া করে এটি একটি সিঙ্ক্রোনাইজড কিছুতে মোড়ানো করবেন না ...

একসাথে সংস্করণে ছুরিকাঘাত এখানে।

import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

public class ConcurrentLruCache<KEY, VALUE> implements LruCache<KEY,VALUE> {

    private final ReentrantLock lock = new ReentrantLock ();


    private final Map<KEY, VALUE> map = new ConcurrentHashMap<> ();
    private final Deque<KEY> queue = new LinkedList<> ();
    private final int limit;


    public ConcurrentLruCache ( int limit ) {
        this.limit = limit;
    }

    @Override
    public void put ( KEY key, VALUE value ) {
        VALUE oldValue = map.put ( key, value );
        if ( oldValue != null ) {
            removeThenAddKey ( key );
        } else {
            addKey ( key );
        }
        if (map.size () > limit) {
            map.remove ( removeLast() );
        }
    }


    @Override
    public VALUE get ( KEY key ) {
        removeThenAddKey ( key );
        return map.get ( key );
    }


    private void addKey(KEY key) {
        lock.lock ();
        try {
            queue.addFirst ( key );
        } finally {
            lock.unlock ();
        }


    }

    private KEY removeLast( ) {
        lock.lock ();
        try {
            final KEY removedKey = queue.removeLast ();
            return removedKey;
        } finally {
            lock.unlock ();
        }
    }

    private void removeThenAddKey(KEY key) {
        lock.lock ();
        try {
            queue.removeFirstOccurrence ( key );
            queue.addFirst ( key );
        } finally {
            lock.unlock ();
        }

    }

    private void removeFirstOccurrence(KEY key) {
        lock.lock ();
        try {
            queue.removeFirstOccurrence ( key );
        } finally {
            lock.unlock ();
        }

    }


    @Override
    public VALUE getSilent ( KEY key ) {
        return map.get ( key );
    }

    @Override
    public void remove ( KEY key ) {
        removeFirstOccurrence ( key );
        map.remove ( key );
    }

    @Override
    public int size () {
        return map.size ();
    }

    public String toString () {
        return map.toString ();
    }
}

প্রধান পার্থক্য হ্যাশম্যাপের পরিবর্তে সাম্প্রতিক হ্যাশম্যাপ ব্যবহার এবং লক ব্যবহার (আমি সিঙ্ক্রোনাইজড হয়ে যেতে পারতাম, তবে ...)।

আমি এটি আগুনের নীচে পরীক্ষা করে দেখিনি, তবে এটি একটি সাধারণ এলআরইউ ক্যাশে বলে মনে হচ্ছে যা ব্যবহারের ক্ষেত্রে 80% এর মধ্যে কার্যকর হতে পারে যেখানে আপনার একটি সাধারণ এলআরইউ মানচিত্র প্রয়োজন।

আপনি কেন লাইব্রেরি a, b, বা c ব্যবহার করবেন না তা বাদ দিয়ে আমি প্রতিক্রিয়া স্বাগত জানাই। আমি সবসময় একটি লাইব্রেরি ব্যবহার না করার কারণ হ'ল আমি সবসময় প্রতিটি যুদ্ধের ফাইল 80MB হওয়া চাই না এবং আমি লাইব্রেরি লিখি যাতে আমি জায়গায় ভাল সমাধানের সাথে লাইবগুলিকে প্লাগ-সক্ষম করতে পারি এবং কেউ প্লাগ করতে পারে - তারা চাইলে অন্য ক্যাশে সরবরাহকারী। :) আমি কখনই জানি না যে কারওর জন্য কখন পেয়ারা বা এহচেচে বা অন্য কোনও কিছুর প্রয়োজন হতে পারে আমি সেগুলি অন্তর্ভুক্ত করতে চাই না, তবে আমি যদি ক্যাচিং প্লাগ-সক্ষম করি তবে আমি সেগুলিও বাদ দেব না।

নির্ভরতা হ্রাসের নিজস্ব পুরষ্কার রয়েছে। এটিকে কীভাবে আরও সহজ বা দ্রুত বা উভয় হিসাবে তৈরি করা যায় সে সম্পর্কে আমি কিছু প্রতিক্রিয়া পেতে পছন্দ করি।

এছাড়াও যদি কেউ যেতে প্রস্তুত জানেন ...

ঠিক আছে .. আমি জানি আপনি কী ভাবছেন ... কেন তিনি কেবল লিঙ্কডহ্যাশম্যাপ থেকে অপসারণের প্রাচীনতম এন্ট্রি ব্যবহার করেন না, এবং ভালই আমার উচিত কিন্তু .... তবে .. তবে .. এটি কোনও ফিফোর হবে আরআরইউ নয় এবং আমরা ছিলাম একটি এলআরইউ বাস্তবায়নের চেষ্টা করছি।

    Map<KEY, VALUE> map = new LinkedHashMap<KEY, VALUE> () {

        @Override
        protected boolean removeEldestEntry ( Map.Entry<KEY, VALUE> eldest ) {
            return this.size () > limit;
        }
    };

এই পরীক্ষাটি উপরের কোডটির জন্য ব্যর্থ হয় ...

        cache.get ( 2 );
        cache.get ( 3 );
        cache.put ( 6, 6 );
        cache.put ( 7, 7 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == null || die ();
        ok |= cache.getSilent ( 5 ) == null || die ();

সুতরাং এখানে রিলিজএলডেস্ট্রি ব্যবহার করে একটি দ্রুত এবং নোংরা ফিফো ক্যাশে রয়েছে।

import java.util.*;

public class FifoCache<KEY, VALUE> implements LruCache<KEY,VALUE> {

    final int limit;

    Map<KEY, VALUE> map = new LinkedHashMap<KEY, VALUE> () {

        @Override
        protected boolean removeEldestEntry ( Map.Entry<KEY, VALUE> eldest ) {
            return this.size () > limit;
        }
    };


    public LruCacheNormal ( int limit ) {
        this.limit = limit;
    }

    public void put ( KEY key, VALUE value ) {
         map.put ( key, value );


    }


    public VALUE get ( KEY key ) {

        return map.get ( key );
    }


    public VALUE getSilent ( KEY key ) {

        return map.get ( key );
    }

    public void remove ( KEY key ) {
        map.remove ( key );
    }

    public int size () {
        return map.size ();
    }

    public String toString() {
        return map.toString ();
    }
}

ফিফো দ্রুত। আশেপাশে কোনও খোঁজ নেই। আপনি একটি এলআরইউয়ের সামনে একটি ফিফোর সামনে দাঁড়াতে পারেন এবং এটি বেশিরভাগ হট এন্ট্রিগুলি বেশ সুন্দরভাবে পরিচালনা করবে। আরও ভাল এলআরইউর জন্য নোড বৈশিষ্ট্যটিতে সেই বিপরীত উপাদানগুলির প্রয়োজন হবে।

যাইহোক ... এখন যেহেতু আমি কিছু কোড লিখেছি, আমাকে অন্য উত্তরগুলি দিয়ে যেতে দাও এবং আমি কী মিস করেছি তা দেখুন ... আমি প্রথমবার সেগুলি স্ক্যান করেছিলাম।


9

LinkedHashMapও (1), তবে সিঙ্ক্রোনাইজেশন প্রয়োজন। সেখানে চাকা পুনর্নবীকরণের দরকার নেই।

সম্মতি বাড়ানোর জন্য 2 টি বিকল্প:

1. একাধিক তৈরি করুন LinkedHashMapসেগুলি, এবং হ্যাশ: উদাহরণ: LinkedHashMap[4], index 0, 1, 2, 3। কোন মানচিত্র কোনও পুট / পেতে / অপসারণ করতে তা বেছে নেওয়ার জন্য কী key%4 (বা binary ORচালু [key, 3]) করুন ।

২. আপনি প্রসারিত করে ConcurrentHashMapএবং এর অভ্যন্তরের প্রতিটি অঞ্চলে একটি লিঙ্কযুক্ত হ্যাশ মানচিত্রের মতো করে একটি 'প্রায়' এলআরইউ করতে পারেন । লকিংটি LinkedHashMapসিঙ্ক্রোনাইজ হওয়ার চেয়ে দানাদারভাবে ঘটবে । একটি putবা putIfAbsentকেবল তালিকার মাথার তালুতে একটি লক প্রয়োজন (প্রতি অঞ্চল)। সরানোর সময় বা পুরো অঞ্চলটিকে লক করা দরকার। আমি আগ্রহী যদি কোনও ধরণের পারমাণবিক লিঙ্কযুক্ত তালিকাগুলি এখানে সহায়তা করতে পারে - সম্ভবত তাই তালিকার শীর্ষস্থানীয়। আরও কিছু জন্য হতে পারে।

কাঠামোটি মোট ক্রমটি রাখবে না, তবে অঞ্চল অনুসারে কেবল আদেশ থাকবে। অঞ্চলগুলির সংখ্যার তুলনায় যতক্ষণ প্রবেশের সংখ্যা অনেক বড়, বেশিরভাগ ক্যাশের ক্ষেত্রে এটি যথেষ্ট ভাল। প্রতিটি অঞ্চলের নিজস্ব প্রবেশের গণনা থাকতে হবে, এটি উচ্ছেদের ট্রিগারের জন্য বিশ্ব গণনার চেয়ে ব্যবহার করা হবে। এ অঞ্চলের ডিফল্ট সংখ্যা ConcurrentHashMap16, যা বর্তমানে বেশিরভাগ সার্ভারের জন্য যথেষ্ট।

  1. মাঝারি সংমিশ্রনের অধীনে লিখতে সহজ এবং দ্রুত হবে।

  2. লিখতে আরও কঠিন হতে পারে তবে খুব উচ্চ সংমিশ্রণে স্কেল আরও ভাল। এটি স্বাভাবিক অ্যাক্সেসের জন্য ধীর হবে (ঠিক ConcurrentHashMapতেমন ধীরে ধীরে HashMapযেখানে সামঞ্জস্য নেই)


8

দুটি ওপেন সোর্স বাস্তবায়ন রয়েছে।

অ্যাপাচি সোলারের সাম্প্রতিক এলআরইউ ক্যাশে রয়েছে: https://lucene.apache.org/solr/3_6_1/org/apache/solr/util/ConcurrentLRUCache.html

কনক্র্যান্টলিঙ্কডহ্যাশম্যাপের জন্য ওপেন সোর্স প্রকল্প রয়েছে: http://code.google.com/p/concurrentlinkedhashmap/


2
সোলারের সমাধানটি আসলে এলআরইউ নয়, তবে এটি ConcurrentLinkedHashMapআকর্ষণীয়। এটি দাবি করে যে এটি MapMakerপেয়ারা থেকে আনা হয়েছে , তবে আমি এটি ডক্সে খুঁজে পাইনি। কোন প্রচেষ্টা কি এই প্রচেষ্টা দিয়ে যাচ্ছে?
হ্যাঙ্ক গে

3
একটি সরলীকৃত সংস্করণ সংহত করা হয়েছিল, তবে পরীক্ষাগুলি এখনও সম্পূর্ণ হয়নি তাই এটি এখনও সর্বজনীন নয়। গভীরতর সংহতকরণে আমার অনেক সমস্যা হয়েছিল, তবে কিছু সুন্দর অ্যালগরিদমিক বৈশিষ্ট্য থাকায় এটি শেষ করার আশা করি। উচ্ছেদের শোনার ক্ষমতা (ক্ষমতা, মেয়াদোত্তীর্ণতা, জিসি) যুক্ত করা হয়েছিল এবং সিএলএইচএমের পদ্ধতির উপর ভিত্তি করে (শ্রোতার সারিতে)। আমি "ওজনিত মান" এর ধারণাকেও অবদান রাখতে চাই, যেহেতু সংগ্রহগুলি ক্যাশে করার সময় এটি কার্যকর। দুর্ভাগ্যক্রমে অন্যান্য প্রতিশ্রুতিগুলির কারণে আমি পেয়ারা প্রাপ্য সময়টি উত্সর্গ করার জন্য খুব বেশি জড়িয়ে পড়েছি (এবং আমি কেভিন / চার্লসকে প্রতিশ্রুতি দিয়েছিলাম)।
বেন মানেস

3
আপডেট: ইন্টিগ্রেশনটি সম্পন্ন হয়েছিল এবং পেয়ারা r08 এ সর্বজনীন। এটি #maximumSize () সেটিং এর মাধ্যমে।
বেন ম্যানস

7

আমি java.util.concurrent.PriorityBlockingQueue ব্যবহার করার বিষয়ে বিবেচনা করব , প্রতিটি উপাদানটিতে একটি "নাম্বার অফস" কাউন্টার দ্বারা অগ্রাধিকার সহ নির্ধারিত। আমার সমস্ত সিঙ্ক্রোনাইজেশন সঠিক হওয়ার জন্য আমি খুব যত্নবান হব, কারণ "নাম্বার অফস" কাউন্টার থেকে বোঝা যাচ্ছে যে উপাদানটি অক্ষর হতে পারে না।

উপাদান উপাদানটি ক্যাশে থাকা সামগ্রীর জন্য একটি মোড়ক হবে:

class CacheElement {
    private final Object obj;
    private int numberOfUsers = 0;

    CacheElement(Object obj) {
        this.obj = obj;
    }

    ... etc.
}

তুমি কি বোঝাতে চাই না যে অপরিবর্তনীয় হতে হবে?
shsteimer

2
মনে রাখবেন যে আপনি যদি স্টিভ ম্লেডোড দ্বারা উল্লিখিত অগ্রাধিকার ব্লকিংকুই সংস্করণটি করার চেষ্টা করেন, আপনার উপাদানটি অপরিবর্তনীয় করা উচিত, কারণ সারিতে থাকা অবস্থায় উপাদানটি পরিবর্তন করা কোনও প্রভাব ফেলবে না, আপনাকে উপাদানটি সরিয়ে নতুন করে যুক্ত করতে হবে এটি আবার অগ্রাধিকার।
জেমস

জেমস নীচে আমার করা একটি ত্রুটি নির্দেশ করেছে। নির্ভরযোগ্য, দৃur় ক্যাশে লেখার পক্ষে রক্তক্ষরণ কতটা কঠিন, তার প্রমাণ হিসাবে আমি এটি পেশ করি।
স্টিভ ম্যাকলিউড

6

আশাকরি এটা সাহায্য করবে .

import java.util.*;
public class Lru {

public static <K,V> Map<K,V> lruCache(final int maxSize) {
    return new LinkedHashMap<K, V>(maxSize*4/3, 0.75f, true) {

        private static final long serialVersionUID = -3588047435434569014L;

        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maxSize;
        }
    };
 }
 public static void main(String[] args ) {
    Map<Object, Object> lru = Lru.lruCache(2);      
    lru.put("1", "1");
    lru.put("2", "2");
    lru.put("3", "3");
    System.out.println(lru);
}
}

1
চমৎকার উদাহরণ! আপনি মন্তব্য করতে পারেন কেন সক্ষমতা সর্বোচ্চ আকার * 4/3 সেট করতে হবে?
আকভেল

1
@Akvel এটা প্রাথমিক ধারণক্ষমতা বলা হচ্ছে, কোনো [পূর্ণসংখ্যা] যেহেতু 0.75f মান ডিফল্ট লোড ফ্যাক্টর আশা করি এই লিঙ্কে সাহায্য করে হতে পারে: ashishsharma.me/2011/09/custom-lru-cache-java.html
murasing

5

এলআরইউ ক্যাশে কনকোর্ন্টলিংকড কিউ এবং কনকন্টার হ্যাশম্যাপ ব্যবহার করে প্রয়োগ করা যেতে পারে যা মাল্টিথ্রেডিং দৃশ্যেও ব্যবহার করা যেতে পারে। সারিটির প্রধান হ'ল সেই উপাদানটি যা দীর্ঘকাল সারিতে রয়েছে। সারিটির লেজটি সেই উপাদানটি যা সারিবদ্ধভাবে সবচেয়ে কম সময়ের মধ্যে ছিল। মানচিত্রে যখন কোনও উপাদান উপস্থিত থাকে, আমরা এটিকে লিঙ্কড কিউয়ে থেকে সরিয়ে লেজটিতে সন্নিবেশ করতে পারি।

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public class LRUCache<K,V> {
  private ConcurrentHashMap<K,V> map;
  private ConcurrentLinkedQueue<K> queue;
  private final int size; 

  public LRUCache(int size) {
    this.size = size;
    map = new ConcurrentHashMap<K,V>(size);
    queue = new ConcurrentLinkedQueue<K>();
  }

  public V get(K key) {
    //Recently accessed, hence move it to the tail
    queue.remove(key);
    queue.add(key);
    return map.get(key);
  }

  public void put(K key, V value) {
    //ConcurrentHashMap doesn't allow null key or values
    if(key == null || value == null) throw new NullPointerException();
    if(map.containsKey(key) {
      queue.remove(key);
    }
    if(queue.size() >= size) {
      K lruKey = queue.poll();
      if(lruKey != null) {
        map.remove(lruKey);
      }
    }
    queue.add(key);
    map.put(key,value);
  }

}

এটি থ্রেডসেফ নয় । উদাহরণস্বরূপ আপনি একই সাথে কল করে সর্বোচ্চ এলআরইউ আকারটি সহজেই অতিক্রম করতে পারেন put
dpeacock

দয়া করে এটি সংশোধন করুন। প্রথমত এটি লাইন map.containsKey (কী) তে সংকলন করে না। দ্বিতীয়ত: (আপনার) কীটি কীটি সত্যিই সরানো হয়েছে কিনা তা যাচাই করা উচিত অন্যথায় মানচিত্র এবং সারি সিঙ্কের বাইরে চলে যায় এবং "কুই.সাইজ ()> আকার" সর্বদা সত্য হয়ে ওঠে। আমি এই দুটি সংগ্রহ ব্যবহার করার আপনার ধারণাটি পছন্দ করেছিলাম বলে আমি আমার সংস্করণটি ঠিক করে দিয়েছি।
আলেকসান্দার

3

এলআরইউর জন্য আমার বাস্তবায়ন এখানে। আমি অগ্রাধিকারের কিউউ ব্যবহার করেছি, যা মূলত ফিফো হিসাবে কাজ করে থ্রেডসেফ নয়। পৃষ্ঠাগুলির তৈরির ভিত্তিতে এবং কমপক্ষে সম্প্রতি ব্যবহৃত সময়ের জন্য পৃষ্ঠাগুলির ক্রম সম্পাদন করে on

বিবেচনার জন্য পৃষ্ঠা: 2, 1, 0, 2, 8, 2, 4

ক্যাশে পৃষ্ঠাগুলি
যুক্ত হল: 2 পৃষ্ঠায় ক্যাশে যুক্ত করা হয়েছে: 1
পৃষ্ঠায় ক্যাশে যুক্ত করা হয়েছে: 0
পৃষ্ঠা: 2 ইতিমধ্যে ক্যাশে উপস্থিত রয়েছে। সর্বশেষ অ্যাক্সেসের সময় আপডেট হওয়া
পৃষ্ঠা ফল্ট, পৃষ্ঠা: 1,
পৃষ্ঠায় প্রতিস্থাপন: 8 পৃষ্ঠায় ক্যাশে যুক্ত করা হয়েছে: 8
পৃষ্ঠা: 2 ইতিমধ্যে ক্যাশে রয়েছে। সর্বশেষ অ্যাক্সেসের সময় আপডেট হওয়া
পৃষ্ঠা ফল্ট, পৃষ্ঠা: 0, পৃষ্ঠার সাথে প্রতিস্থাপন: 4
পৃষ্ঠায় ক্যাশে যুক্ত করা হয়েছে: 4

আউটপুট

LRUCache পৃষ্ঠাগুলি
------------- পৃষ্ঠা
নাম: 8, পেজ তৈরিকরণ সময়: 1365957019974 পৃষ্ঠা
নাম: 2, পৃষ্ঠা সৃজন টাইম: 1365957020074 পৃষ্ঠা
নাম: 4, পৃষ্ঠা সৃজন টাইম: 1365957020174

এখানে কোড লিখুন

import java.util.Comparator;
import java.util.Iterator;
import java.util.PriorityQueue;


public class LRUForCache {
    private PriorityQueue<LRUPage> priorityQueue = new PriorityQueue<LRUPage>(3, new LRUPageComparator());
    public static void main(String[] args) throws InterruptedException {

        System.out.println(" Pages for consideration : 2, 1, 0, 2, 8, 2, 4");
        System.out.println("----------------------------------------------\n");

        LRUForCache cache = new LRUForCache();
        cache.addPageToQueue(new LRUPage("2"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("1"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("0"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("2"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("8"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("2"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("4"));
        Thread.sleep(100);

        System.out.println("\nLRUCache Pages");
        System.out.println("-------------");
        cache.displayPriorityQueue();
    }


    public synchronized void  addPageToQueue(LRUPage page){
        boolean pageExists = false;
        if(priorityQueue.size() == 3){
            Iterator<LRUPage> iterator = priorityQueue.iterator();

            while(iterator.hasNext()){
                LRUPage next = iterator.next();
                if(next.getPageName().equals(page.getPageName())){
                    /* wanted to just change the time, so that no need to poll and add again.
                       but elements ordering does not happen, it happens only at the time of adding
                       to the queue

                       In case somebody finds it, plz let me know.
                     */
                    //next.setPageCreationTime(page.getPageCreationTime()); 

                    priorityQueue.remove(next);
                    System.out.println("Page: " + page.getPageName() + " already exisit in cache. Last accessed time updated");
                    pageExists = true;
                    break;
                }
            }
            if(!pageExists){
                // enable it for printing the queue elemnts
                //System.out.println(priorityQueue);
                LRUPage poll = priorityQueue.poll();
                System.out.println("Page Fault, PAGE: " + poll.getPageName()+", Replaced with PAGE: "+page.getPageName());

            }
        }
        if(!pageExists){
            System.out.println("Page added into cache is : " + page.getPageName());
        }
        priorityQueue.add(page);

    }

    public void displayPriorityQueue(){
        Iterator<LRUPage> iterator = priorityQueue.iterator();
        while(iterator.hasNext()){
            LRUPage next = iterator.next();
            System.out.println(next);
        }
    }
}

class LRUPage{
    private String pageName;
    private long pageCreationTime;
    public LRUPage(String pagename){
        this.pageName = pagename;
        this.pageCreationTime = System.currentTimeMillis();
    }

    public String getPageName() {
        return pageName;
    }

    public long getPageCreationTime() {
        return pageCreationTime;
    }

    public void setPageCreationTime(long pageCreationTime) {
        this.pageCreationTime = pageCreationTime;
    }

    @Override
    public boolean equals(Object obj) {
        LRUPage page = (LRUPage)obj; 
        if(pageCreationTime == page.pageCreationTime){
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return (int) (31 * pageCreationTime);
    }

    @Override
    public String toString() {
        return "PageName: " + pageName +", PageCreationTime: "+pageCreationTime;
    }
}


class LRUPageComparator implements Comparator<LRUPage>{

    @Override
    public int compare(LRUPage o1, LRUPage o2) {
        if(o1.getPageCreationTime() > o2.getPageCreationTime()){
            return 1;
        }
        if(o1.getPageCreationTime() < o2.getPageCreationTime()){
            return -1;
        }
        return 0;
    }
}

2

এখানে আমার পরীক্ষিত সেরা পারফরম্যান্স সমকালীন LRU ক্যাশে বাস্তবায়নটি কোনও সিঙ্ক্রোনাইজড ব্লক ছাড়াই:

public class ConcurrentLRUCache<Key, Value> {

private final int maxSize;

private ConcurrentHashMap<Key, Value> map;
private ConcurrentLinkedQueue<Key> queue;

public ConcurrentLRUCache(final int maxSize) {
    this.maxSize = maxSize;
    map = new ConcurrentHashMap<Key, Value>(maxSize);
    queue = new ConcurrentLinkedQueue<Key>();
}

/**
 * @param key - may not be null!
 * @param value - may not be null!
 */
public void put(final Key key, final Value value) {
    if (map.containsKey(key)) {
        queue.remove(key); // remove the key from the FIFO queue
    }

    while (queue.size() >= maxSize) {
        Key oldestKey = queue.poll();
        if (null != oldestKey) {
            map.remove(oldestKey);
        }
    }
    queue.add(key);
    map.put(key, value);
}

/**
 * @param key - may not be null!
 * @return the value associated to the given key or null
 */
public Value get(final Key key) {
    return map.get(key);
}

}


1
@ জোলতান বোদা .... আপনি একটি পরিস্থিতি সামাল দেন নি .. যদি একই জিনিসটি একাধিকবার ব্যবহার হয় তবে কী হবে? এক্ষেত্রে আমাদের একই বস্তুর জন্য একাধিক এন্ট্রি যুক্ত করা উচিত নয় ... পরিবর্তে এর কীটি হওয়া উচিত

5
সতর্কতা: এটি কোনও এলআরইউ ক্যাশে নয়। একটি এলআরইউ ক্যাশে, আপনি সম্প্রতি অ্যাক্সেস করা আইটেমগুলি ফেলে দেন। এটি সর্বশেষে লেখা আইটেমগুলি ফেলে দেয়। এটি কিউ.রেমোভ (কী) ক্রিয়াকলাপ সম্পাদন করার জন্য একটি লিনিয়ার স্ক্যান।
ডেভ এল।

এছাড়াও একযোগে লিঙ্কযুক্ত কিউ # আকার () একটি ধ্রুবক সময় ক্রিয়াকলাপ নয়।
নাটস

3
আপনার পুট পদ্ধতিটি নিরাপদ দেখাচ্ছে না - এটিতে কয়েকটি পরীক্ষা-পরবর্তী বিবৃতি রয়েছে যা একাধিক থ্রেডের সাথে ভেঙে যাবে।
Assylias

2

এটি আমি ব্যবহার করি এমন এলআরইউ ক্যাশে, যা একটি লিঙ্কডহ্যাশম্যাপকে সজ্জিত করে এবং সরস দাগগুলি রক্ষা করে একটি সাধারণ সিঙ্ক্রোনাইজ লক দিয়ে একত্রে সামঞ্জস্য করে। এটি উপাদানগুলিকে ব্যবহৃত হওয়ার সাথে সাথে এটি "স্পর্শ" করে যাতে তারা আবার "সর্বাধিক" উপাদান হয়ে যায়, যাতে এটি আসলে এলআরইউ হয়। আমার ন্যূনতম আজীবন থাকার সাথে আমার উপাদানগুলির প্রয়োজনীয়তাও ছিল যা আপনি "সর্বাধিক অলস সময়" হিসাবে অনুমোদিত হিসাবে বিবেচনা করতে পারেন, তারপরে আপনি উচ্ছেদের জন্য প্রস্তুত।

তবে আমি হ্যাঙ্কের উপসংহার এবং স্বীকৃত উত্তরের সাথে একমত - যদি আজ আমি আবার এটি শুরু করতাম তবে আমি পেয়ারা পরীক্ষা করতাম CacheBuilder

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


public class MaxIdleLRUCache<KK, VV> {

    final static private int IDEAL_MAX_CACHE_ENTRIES = 128;

    public interface DeadElementCallback<KK, VV> {
        public void notify(KK key, VV element);
    }

    private Object lock = new Object();
    private long minAge;
    private HashMap<KK, Item<VV>> cache;


    public MaxIdleLRUCache(long minAgeMilliseconds) {
        this(minAgeMilliseconds, IDEAL_MAX_CACHE_ENTRIES);
    }

    public MaxIdleLRUCache(long minAgeMilliseconds, int idealMaxCacheEntries) {
        this(minAgeMilliseconds, idealMaxCacheEntries, null);
    }

    public MaxIdleLRUCache(long minAgeMilliseconds, int idealMaxCacheEntries, final DeadElementCallback<KK, VV> callback) {
        this.minAge = minAgeMilliseconds;
        this.cache = new LinkedHashMap<KK, Item<VV>>(IDEAL_MAX_CACHE_ENTRIES + 1, .75F, true) {
            private static final long serialVersionUID = 1L;

            // This method is called just after a new entry has been added
            public boolean removeEldestEntry(Map.Entry<KK, Item<VV>> eldest) {
                // let's see if the oldest entry is old enough to be deleted. We don't actually care about the cache size.
                long age = System.currentTimeMillis() - eldest.getValue().birth;
                if (age > MaxIdleLRUCache.this.minAge) {
                    if ( callback != null ) {
                        callback.notify(eldest.getKey(), eldest.getValue().payload);
                    }
                    return true; // remove it
                }
                return false; // don't remove this element
            }
        };

    }

    public void put(KK key, VV value) {
        synchronized ( lock ) {
//          System.out.println("put->"+key+","+value);
            cache.put(key, new Item<VV>(value));
        }
    }

    public VV get(KK key) {
        synchronized ( lock ) {
//          System.out.println("get->"+key);
            Item<VV> item = getItem(key);
            return item == null ? null : item.payload;
        }
    }

    public VV remove(String key) {
        synchronized ( lock ) {
//          System.out.println("remove->"+key);
            Item<VV> item =  cache.remove(key);
            if ( item != null ) {
                return item.payload;
            } else {
                return null;
            }
        }
    }

    public int size() {
        synchronized ( lock ) {
            return cache.size();
        }
    }

    private Item<VV> getItem(KK key) {
        Item<VV> item = cache.get(key);
        if (item == null) {
            return null;
        }
        item.touch(); // idle the item to reset the timeout threshold
        return item;
    }

    private static class Item<T> {
        long birth;
        T payload;

        Item(T payload) {
            this.birth = System.currentTimeMillis();
            this.payload = payload;
        }

        public void touch() {
            this.birth = System.currentTimeMillis();
        }
    }

}

2

ঠিক আছে ক্যাশের জন্য আপনি সাধারণত একটি প্রক্সি অবজেক্টের মাধ্যমে কিছু টুকরো ডেটা খুঁজছেন, (একটি ইউআরএল, স্ট্রিং ....) সুতরাং ইন্টারফেস-ভিত্তিতে আপনি কোনও মানচিত্র চান। তবে জিনিসগুলি কিক করতে আপনার কাঠামোর মতো একটি সারি চাই। অভ্যন্তরীণভাবে আমি দুটি ডেটা স্ট্রাকচার, একটি অগ্রাধিকার-ক্যু এবং একটি হ্যাশম্যাপ পরিচালনা করব। এখানে একটি বাস্তবায়ন যা ও (1) সময়ে সমস্ত কিছু করতে সক্ষম হওয়া উচিত।

এখানে একটি ক্লাস আমি বেশ দ্রুত বেত্রাঘাত করেছি:

import java.util.HashMap;
import java.util.Map;
public class LRUCache<K, V>
{
    int maxSize;
    int currentSize = 0;

    Map<K, ValueHolder<K, V>> map;
    LinkedList<K> queue;

    public LRUCache(int maxSize)
    {
        this.maxSize = maxSize;
        map = new HashMap<K, ValueHolder<K, V>>();
        queue = new LinkedList<K>();
    }

    private void freeSpace()
    {
        K k = queue.remove();
        map.remove(k);
        currentSize--;
    }

    public void put(K key, V val)
    {
        while(currentSize >= maxSize)
        {
            freeSpace();
        }
        if(map.containsKey(key))
        {//just heat up that item
            get(key);
            return;
        }
        ListNode<K> ln = queue.add(key);
        ValueHolder<K, V> rv = new ValueHolder<K, V>(val, ln);
        map.put(key, rv);       
        currentSize++;
    }

    public V get(K key)
    {
        ValueHolder<K, V> rv = map.get(key);
        if(rv == null) return null;
        queue.remove(rv.queueLocation);
        rv.queueLocation = queue.add(key);//this ensures that each item has only one copy of the key in the queue
        return rv.value;
    }
}

class ListNode<K>
{
    ListNode<K> prev;
    ListNode<K> next;
    K value;
    public ListNode(K v)
    {
        value = v;
        prev = null;
        next = null;
    }
}

class ValueHolder<K,V>
{
    V value;
    ListNode<K> queueLocation;
    public ValueHolder(V value, ListNode<K> ql)
    {
        this.value = value;
        this.queueLocation = ql;
    }
}

class LinkedList<K>
{
    ListNode<K> head = null;
    ListNode<K> tail = null;

    public ListNode<K> add(K v)
    {
        if(head == null)
        {
            assert(tail == null);
            head = tail = new ListNode<K>(v);
        }
        else
        {
            tail.next = new ListNode<K>(v);
            tail.next.prev = tail;
            tail = tail.next;
            if(tail.prev == null)
            {
                tail.prev = head;
                head.next = tail;
            }
        }
        return tail;
    }

    public K remove()
    {
        if(head == null)
            return null;
        K val = head.value;
        if(head.next == null)
        {
            head = null;
            tail = null;
        }
        else
        {
            head = head.next;
            head.prev = null;
        }
        return val;
    }

    public void remove(ListNode<K> ln)
    {
        ListNode<K> prev = ln.prev;
        ListNode<K> next = ln.next;
        if(prev == null)
        {
            head = next;
        }
        else
        {
            prev.next = next;
        }
        if(next == null)
        {
            tail = prev;
        }
        else
        {
            next.prev = prev;
        }       
    }
}

এখানে কিভাবে এটা কাজ করে. কীগুলি তালিকার সামনের পুরানো কীগুলির সাথে একটি লিঙ্কযুক্ত তালিকায় সংরক্ষণ করা হয় (নতুন কীগুলি পিছনে যায়) সুতরাং যখন আপনাকে কিছু বের করে আনতে হবে তখন আপনি এটিকে সারিটির সামনের অংশ থেকে সরিয়ে ফেলুন এবং তারপরে কীটি ব্যবহার করুন মানচিত্র থেকে মানটি সরিয়ে দিন। যখন কোনও আইটেমটি রেফারেন্স হয়ে যায় আপনি মানচিত্র থেকে ভ্যালুহোল্ডারটি ধরে ফেলেন এবং তারপরে কুইলের বর্তমান অবস্থান থেকে কীটি সরিয়ে ফেলতে কুইলোকেশন ভেরিয়েবলটি ব্যবহার করুন এবং তারপরে এটিকে সারির পিছনে রাখুন (এটি বর্তমানে ব্যবহৃত সবচেয়ে বেশি ব্যবহৃত হয়েছে)। জিনিস যোগ করা প্রায় একই রকম।

আমি নিশ্চিত যে এখানে প্রচুর ত্রুটি রয়েছে এবং আমি কোনও সিঙ্ক্রোনাইজেশন প্রয়োগ করি নি। তবে এই শ্রেণিটি ক্যাশে যুক্ত ও (1), ও (1) পুরানো আইটেমগুলি অপসারণ এবং ও (1) ক্যাশে আইটেমগুলি পুনরুদ্ধার সরবরাহ করবে। এমনকি একটি তুচ্ছ সিঙ্ক্রোনাইজেশন (কেবলমাত্র প্রতিটি পাবলিক পদ্ধতিতে সিঙ্ক্রোনাইজ করা) এখনও চালানোর সময়টির কারণে সামান্য লকযুক্তি থাকতে পারে। কারও কাছে যদি কোনও চৌকস সিঙ্ক্রোনাইজেশন কৌশল থাকে তবে আমি খুব আগ্রহী। এছাড়াও, আমি নিশ্চিত যে কিছু অতিরিক্ত অপ্টিমাইজেশন রয়েছে যা আপনি মানচিত্রের সাথে সর্বাধিক আকার পরিবর্তনশীল ব্যবহার করে প্রয়োগ করতে পারেন।


বিশদ স্তরের জন্য ধন্যবাদ, তবে এটি LinkedHashMap+ Collections.synchronizedMap()বাস্তবায়নের উপরে জয় কোথায় দেয় ?
হ্যাঙ্ক গে

পারফরম্যান্স, আমি নিশ্চিতভাবে জানি না, তবে আমি মনে করি না লিংকডহ্যাশম্যাপে ও (1) সন্নিবেশ রয়েছে (এটি সম্ভবত ও (লগ (এন))) আছে, আসলে আপনি আমার বাস্তবায়নে মানচিত্র ইন্টারফেসটি সম্পূর্ণ করতে কয়েকটি পদ্ধতি যুক্ত করতে পারেন এবং তারপরে সম্মতি যুক্ত করতে কালেকশন.সিনক্রোনাইজড ম্যাপ ব্যবহার করুন।
luke

অ্যাড পদ্ধতিতে উপরের লিঙ্কডলিস্ট ক্লাসে অন্য ব্লকের একটি কোড রয়েছে যেমন যদি (লেজ.প্রিভ == নাল) {টেল.প্রিভ = হেড; head.next = লেজ; This এই কোডটি কখন কার্যকর হবে? আমি কয়েকটি শুকনো রান দৌড়েছি এবং আমি মনে করি এটি কখনই কার্যকর হবে না এবং তা সরানো উচিত।
দিপেশ

1

কটাক্ষপাত আছে ConcurrentSkipListMap । এটি ইতিমধ্যে ক্যাশে থাকা কোনও উপাদানকে পরীক্ষার এবং অপসারণের জন্য লগ (এন) সময় দেওয়া উচিত এবং এটি পুনরায় যুক্ত করার জন্য ধ্রুব সময় time

এলআরইউ অর্ডার জোর করে চাপিয়ে দেওয়ার জন্য এবং ক্যাশে পূর্ণ হওয়ার পরে সাম্প্রতিক জিনিসগুলি বাতিল করা হয়েছে তা নিশ্চিত করার জন্য আপনাকে কেবল কিছু কাউন্টার ইত্যাদি এবং মোড়কের উপাদান দরকার।


চান ConcurrentSkipListMapকিছু আরাম অফ বাস্তবায়ন সুবিধা প্রদান ConcurrentHashMap, অথবা এটি কেবল আবেগপূর্ণ ক্ষেত্রে এড়ানো এর ক্ষেত্রে দেখা যায়?
হ্যাঙ্ক গে

এটি জিনিসগুলিকে সহজ করে তুলবে, কনকন্টারস্কিপলিস্টম্যাপ আদেশের উপাদান হিসাবে, যা আপনাকে কোন আদেশে কী জিনিস ব্যবহার করা হত তা পরিচালনা করতে দেয় Con সাম্প্রতিক হ্যাশম্যাপ এটি করে না, সুতরাং আপনাকে মূলত কোনও উপাদানগুলির শেষের আপডেট করতে পুরো ক্যাশে সামগ্রীতে পুনরাবৃত্তি করতে হবে ব্যবহার করা হয়েছে কাউন্টার 'বা যাই হোক না কেন
ম্যাডলেপ

সুতরাং ConcurrentSkipListMapবাস্তবায়নের সাথে সাথে , আমি Mapইন্টারফেসের একটি নতুন বাস্তবায়ন তৈরি করব যা প্রতিনিয়ত প্রতিনিধিত্ব করে ConcurrentSkipListMapএবং কিছু প্রকারের মোড়কের কাজ করে যাতে স্বেচ্ছাসেবক কী প্রকারগুলি এমন এক প্রকারে মোড়ানো যায় যা শেষ অ্যাক্সেসের ভিত্তিতে সহজেই সাজানো হয়?
হ্যাঙ্ক গে

1

এখানে আমার সংক্ষিপ্ত বাস্তবায়ন, দয়া করে সমালোচনা বা উন্নত করুন!

package util.collection;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Limited size concurrent cache map implementation.<br/>
 * LRU: Least Recently Used.<br/>
 * If you add a new key-value pair to this cache after the maximum size has been exceeded,
 * the oldest key-value pair will be removed before adding.
 */

public class ConcurrentLRUCache<Key, Value> {

private final int maxSize;
private int currentSize = 0;

private ConcurrentHashMap<Key, Value> map;
private ConcurrentLinkedQueue<Key> queue;

public ConcurrentLRUCache(final int maxSize) {
    this.maxSize = maxSize;
    map = new ConcurrentHashMap<Key, Value>(maxSize);
    queue = new ConcurrentLinkedQueue<Key>();
}

private synchronized void freeSpace() {
    Key key = queue.poll();
    if (null != key) {
        map.remove(key);
        currentSize = map.size();
    }
}

public void put(Key key, Value val) {
    if (map.containsKey(key)) {// just heat up that item
        put(key, val);
        return;
    }
    while (currentSize >= maxSize) {
        freeSpace();
    }
    synchronized(this) {
        queue.add(key);
        map.put(key, val);
        currentSize++;
    }
}

public Value get(Key key) {
    return map.get(key);
}
}

1
এটি এলআরইউ ক্যাশে নয়, কেবল ফিফো ক্যাশে।
lslab

1

এই সমস্যাটিতে আমার নিজের প্রয়োগ রয়েছে

সিম্পল্রুচে টিটিএল সমর্থন সহ থ্রেডসেফ, খুব সাধারণ, বিতরণবিহীন এলআরইউ ক্যাচ সরবরাহ করে। এটি দুটি বাস্তবায়ন সরবরাহ করে:

  • সাম্প্রতিক লিঙ্কডহ্যাশম্যাপের উপর ভিত্তি করে সমকালীন
  • লিংকডহ্যাশম্যাপের উপর ভিত্তি করে সিঙ্ক্রোনাইজ করা

আপনি এটি এখানে খুঁজে পেতে পারেন: http://code.google.com/p/simplelrucache/


1

অর্জনের সর্বোত্তম উপায় হ'ল লিংকডহ্যাশম্যাপ ব্যবহার করা যা উপাদানগুলির সন্নিবেশ ক্রম বজায় রাখে। নিম্নলিখিতটি একটি নমুনা কোড রয়েছে:

public class Solution {

Map<Integer,Integer> cache;
int capacity;
public Solution(int capacity) {
    this.cache = new LinkedHashMap<Integer,Integer>(capacity); 
    this.capacity = capacity;

}

// This function returns false if key is not 
// present in cache. Else it moves the key to 
// front by first removing it and then adding 
// it, and returns true. 

public int get(int key) {
if (!cache.containsKey(key)) 
        return -1; 
    int value = cache.get(key);
    cache.remove(key); 
    cache.put(key,value); 
    return cache.get(key); 

}

public void set(int key, int value) {

    // If already present, then  
    // remove it first we are going to add later 
       if(cache.containsKey(key)){
        cache.remove(key);
    }
     // If cache size is full, remove the least 
    // recently used. 
    else if (cache.size() == capacity) { 
        Iterator<Integer> iterator = cache.keySet().iterator();
        cache.remove(iterator.next()); 
    }
        cache.put(key,value);
}

}


0

আমি জাভা কোড ব্যবহার করে আরও ভাল LRU ক্যাশে খুঁজছি। আপনার জাভা এলআরইউ ক্যাশে কোড ব্যবহার করে LinkedHashMapএবং ভাগ করে নেওয়া কি সম্ভব Collections#synchronizedMap? বর্তমানে আমি ব্যবহার করছি LRUMap implements Mapএবং কোডটি সূক্ষ্মভাবে কাজ ArrayIndexOutofBoundExceptionকরে তবে নীচের পদ্ধতিতে 500 জন ব্যবহারকারী ব্যবহার করে লোড টেস্টিংয়ে যাচ্ছি । পদ্ধতিটি সাম্প্রতিক বস্তুটিকে সারির সামনে নিয়ে যায়।

private void moveToFront(int index) {
        if (listHead != index) {
            int thisNext = nextElement[index];
            int thisPrev = prevElement[index];
            nextElement[thisPrev] = thisNext;
            if (thisNext >= 0) {
                prevElement[thisNext] = thisPrev;
            } else {
                listTail = thisPrev;
            }
            //old listHead and new listHead say new is 1 and old was 0 then prev[1]= 1 is the head now so no previ so -1
            // prev[0 old head] = new head right ; next[new head] = old head
            prevElement[index] = -1;
            nextElement[index] = listHead;
            prevElement[listHead] = index;
            listHead = index;
        }
    }

get(Object key)এবং put(Object key, Object value)পদ্ধতি উপরের পদ্ধতি কল করে moveToFront


0

হ্যাঙ্কের দেওয়া উত্তরে মন্তব্য যুক্ত করতে চেয়েছিল তবে আমি কীভাবে পারছি না এমন কিছু - দয়া করে এটি মন্তব্য হিসাবে বিবেচনা করুন

লিংকডহ্যাশম্যাপ তার কন্সট্রাক্টরে পাস হওয়া প্যারামিটারের ভিত্তিতে অ্যাক্সেস অর্ডার বজায় রাখে এটি অর্ডার বজায় রাখতে দ্বিগুণ রেখাযুক্ত তালিকা রাখে (লিঙ্কডহ্যাশম্যাপ.এন্ট্রি দেখুন)

@ পেসারিয়র এটি সঠিক যে লিংকডহ্যাশম্যাপ একই আদেশ রাখে যখন পুনরায় পুনরুক্তি করা হয় যদি উপাদানটি আবার যুক্ত করা হয় তবে এটি কেবল সন্নিবেশ আদেশ ক্রমের ক্ষেত্রেই হয়।

এটি আমি লিঙ্কডহ্যাশম্যাপ.এন্ট্রি অবজেক্টের জাভা ডক্সে পেয়েছি

    /**
     * This method is invoked by the superclass whenever the value
     * of a pre-existing entry is read by Map.get or modified by Map.set.
     * If the enclosing Map is access-ordered, it moves the entry
     * to the end of the list; otherwise, it does nothing.
     */
    void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) {
            lm.modCount++;
            remove();
            addBefore(lm.header);
        }
    }

এই পদ্ধতিটি সম্প্রতি অ্যাক্সেস করা উপাদানটিকে তালিকার শেষের দিকে নিয়ে যাওয়ার যত্ন নেয়। সুতরাং সমস্ত লিঙ্কডহ্যাশম্যাপই এলআরইউ ক্যাশে প্রয়োগের জন্য সেরা ডেটা স্ট্রাকচার।


0

জাভা সম্পর্কিত লিঙ্কডহ্যাশম্যাপ সংগ্রহ ব্যবহার করে অন্য একটি চিন্তাভাবনা এমনকি একটি সাধারণ বাস্তবায়ন।

লিংকডহ্যাশম্যাপ সরবরাহ পদ্ধতি EldestEntry প্রদান করে এবং যা উদাহরণে উল্লিখিত পদ্ধতিতে ওভাররাইড করা যেতে পারে। ডিফল্টরূপে এই সংগ্রহ কাঠামোর বাস্তবায়ন মিথ্যা। যদি এই কাঠামোর প্রকৃত এবং আকার প্রারম্ভিক বা পুরানো উপাদানগুলির তুলনায় প্রাথমিক ক্ষমতা ছাড়িয়ে যায় তবে এটি সরানো হবে।

আমাদের ক্ষেত্রে পেজনো এবং পৃষ্ঠাগুলি থাকতে পারে পেজনো পূর্ণসংখ্যা এবং পেজ কনটেন্টে আমি পৃষ্ঠা নম্বর মানের স্ট্রিং রেখেছি।

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author Deepak Singhvi
 *
 */
public class LRUCacheUsingLinkedHashMap {


     private static int CACHE_SIZE = 3;
     public static void main(String[] args) {
        System.out.println(" Pages for consideration : 2, 1, 0, 2, 8, 2, 4,99");
        System.out.println("----------------------------------------------\n");


// accessOrder is true, so whenever any page gets changed or accessed,    // its order will change in the map, 
              LinkedHashMap<Integer,String> lruCache = new              
                 LinkedHashMap<Integer,String>(CACHE_SIZE, .75F, true) {

           private static final long serialVersionUID = 1L;

           protected boolean removeEldestEntry(Map.Entry<Integer,String>                           

                     eldest) {
                          return size() > CACHE_SIZE;
                     }

                };

  lruCache.put(2, "2");
  lruCache.put(1, "1");
  lruCache.put(0, "0");
  System.out.println(lruCache + "  , After first 3 pages in cache");
  lruCache.put(2, "2");
  System.out.println(lruCache + "  , Page 2 became the latest page in the cache");
  lruCache.put(8, "8");
  System.out.println(lruCache + "  , Adding page 8, which removes eldest element 2 ");
  lruCache.put(2, "2");
  System.out.println(lruCache+ "  , Page 2 became the latest page in the cache");
  lruCache.put(4, "4");
  System.out.println(lruCache+ "  , Adding page 4, which removes eldest element 1 ");
  lruCache.put(99, "99");
  System.out.println(lruCache + " , Adding page 99, which removes eldest element 8 ");

     }

}

উপরোক্ত কোড কার্যকর করার ফলাফল নিম্নরূপ:

 Pages for consideration : 2, 1, 0, 2, 8, 2, 4,99
--------------------------------------------------
    {2=2, 1=1, 0=0}  , After first 3 pages in cache
    {2=2, 1=1, 0=0}  , Page 2 became the latest page in the cache
    {1=1, 0=0, 8=8}  , Adding page 8, which removes eldest element 2 
    {0=0, 8=8, 2=2}  , Page 2 became the latest page in the cache
    {8=8, 2=2, 4=4}  , Adding page 4, which removes eldest element 1 
    {2=2, 4=4, 99=99} , Adding page 99, which removes eldest element 8 

এটি একটি ফিফো। তিনি এলআরইউ চেয়েছিলেন।
রিকহাই

এটি এই পরীক্ষায় ব্যর্থ হয় ... cache.get (2); cache.get (3); ক্যাশে.পুট (6, 6); ক্যাশে.পুট (7, 7); ঠিক আছে | = cache.size () == 4 || ডাই ("আকার" + ক্যাশে.সাইজ ()); ঠিক আছে | = cache.getSilent (2) == 2 || ডাই (); ঠিক আছে | = cache.getSilent (3) == 3 || ডাই (); ঠিক আছে | = cache.getSilent (4) == নাল || ডাই (); ঠিক আছে | = cache.getSilent (5) == নাল || ডাই ();
রিকহাই

0

@ সানজানব ধারণাটি অনুসরণ করে (তবে সংশোধন করার পরে) আমি আমার এলআরইউ ক্যাশেটির সংস্করণটি গ্রাহককে সরবরাহ করে যা প্রয়োজনে মুছে ফেলা আইটেমগুলির সাথে কিছু করার অনুমতি দেয়।

public class LRUCache<K, V> {

    private ConcurrentHashMap<K, V> map;
    private final Consumer<V> onRemove;
    private ConcurrentLinkedQueue<K> queue;
    private final int size;

    public LRUCache(int size, Consumer<V> onRemove) {
        this.size = size;
        this.onRemove = onRemove;
        this.map = new ConcurrentHashMap<>(size);
        this.queue = new ConcurrentLinkedQueue<>();
    }

    public V get(K key) {
        //Recently accessed, hence move it to the tail
        if (queue.remove(key)) {
            queue.add(key);
            return map.get(key);
        }
        return null;
    }

    public void put(K key, V value) {
        //ConcurrentHashMap doesn't allow null key or values
        if (key == null || value == null) throw new IllegalArgumentException("key and value cannot be null!");

        V existing = map.get(key);
        if (existing != null) {
            queue.remove(key);
            onRemove.accept(existing);
        }

        if (map.size() >= size) {
            K lruKey = queue.poll();
            if (lruKey != null) {
                V removed = map.remove(lruKey);
                onRemove.accept(removed);
            }
        }
        queue.add(key);
        map.put(key, value);
    }
}

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.