তালিকা <টি> .আপনি () খুব ধীর?


94

জেনেরিকস List.Contains()ফাংশনটি এত ধীরে কেন কেউ আমাকে ব্যাখ্যা করতে পারে ?

আমার List<long>প্রায় এক মিলিয়ন সংখ্যা রয়েছে এবং কোডগুলি এই সংখ্যাগুলির মধ্যে একটি নির্দিষ্ট নম্বর আছে কিনা তা নিয়মিত যাচাই করে চলেছে।

আমি একই জিনিস Dictionary<long, byte>এবং Dictionary.ContainsKey()ফাংশনটি ব্যবহার করার চেষ্টা করেছি এবং এটি তালিকার তুলনায় প্রায় 10-20 গুণ বেশি দ্রুত ছিল faster

অবশ্যই, আমি আসলে সেই উদ্দেশ্যে অভিধান ব্যবহার করতে চাই না, কারণ এটি সেভাবে ব্যবহার করা হয়নি।

সুতরাং, এখানে আসল প্রশ্নটি হ'ল, এর কোনও বিকল্প আছে List<T>.Contains(), তবে ততটা হতাশ নয় Dictionary<K,V>.ContainsKey()?


4
অভিধানে কী সমস্যা? এটি আপনার মতো ক্ষেত্রে ব্যবহারের উদ্দেশ্যে।
কামারে

4
@ কামারে: হ্যাশসেট একটি ভাল বিকল্প হতে পারে।
ব্রায়ান রাসমুসেন

হ্যাশসেট আমি যা খুঁজছিলাম।
ডিএসেন্ট

উত্তর:


160

যদি আপনি কেবল অস্তিত্বের জন্য যাচাই করে থাকেন, HashSet<T>.NET 3.5 এ আপনার সেরা বিকল্প - অভিধানের মতো পারফরম্যান্স, তবে কোনও কী / মান জুটি নেই - কেবল মানগুলি:

    HashSet<int> data = new HashSet<int>();
    for (int i = 0; i < 1000000; i++)
    {
        data.Add(rand.Next(50000000));
    }
    bool contains = data.Contains(1234567); // etc

30

List.Contains একটি ও (এন) অপারেশন।

অভিধান. কনটেনসকি একটি ও (1) অপারেশন, যেহেতু এটি বস্তুর হ্যাশকোডকে কী হিসাবে ব্যবহার করে, যা আপনাকে দ্রুত অনুসন্ধানের ক্ষমতা দেয়।

আমি মনে করি না যে দশ মিলিয়ন এন্ট্রি রয়েছে এমন একটি তালিকা থাকা ভাল ধারণা। আমি মনে করি না যে তালিকা ক্লাসটি সেই উদ্দেশ্যে তৈরি করা হয়েছিল। :)

উদাহরণস্বরূপ এই মিলন সত্তাগুলি কোনও আরডিবিএমএসে সংরক্ষণ করা এবং সেই ডাটাবেসে কোয়েরি করা কি সম্ভব নয়?

যদি এটি সম্ভব না হয়, তবে আমি যাই হোক না কেন একটি অভিধান ব্যবহার করব।


13
আমি মনে করি না মিলিয়ন আইটেমযুক্ত তালিকার কোনও অনুপযুক্ত কিছু নেই, এটি সম্ভবত আপনি এটি জুড়ে রৈখিক অনুসন্ধান চালিয়ে যেতে চান না।
ডিন হবে

সম্মত হন, তালিকার সাথে অনেক কিছুই বা এন্ট্রি সহ কোনও অ্যারে নেই। শুধু মানগুলির জন্য স্ক্যান করবেন না।
মাইকেল ক্রাকলিস

8

আমি মনে করি আমার উত্তর আছে! হ্যাঁ, এটি সত্য যে একটি তালিকায় (অ্যারে) অন্তর্ভুক্ত () রয়েছে ও (এন), তবে যদি অ্যারেটি সংক্ষিপ্ত হয় এবং আপনি মান ধরণের ব্যবহার করেন তবে এটি এখনও খুব দ্রুত হওয়া উচিত। তবে সিএলআর প্রোফাইলার [মাইক্রোসফ্ট থেকে ফ্রি ডাউনলোড] ব্যবহার করে আমি আবিষ্কার করেছি যে কন্টেন্টগুলি () তাদের তুলনা করার জন্য বক্সিং মান রয়েছে যার জন্য গাদা বরাদ্দ প্রয়োজন যা খুব ব্যয়বহুল (ধীর)। [দ্রষ্টব্য: এটি। নেট 2.0; অন্যান্য। নেট সংস্করণ পরীক্ষা করা হয়নি।]

এখানে সম্পূর্ণ গল্প এবং সমাধান। আমাদের কাছে "VI" নামক একটি গণনা রয়েছে এবং "ValueIdList" নামে একটি শ্রেণি তৈরি করা হয়েছে যা VI objects অবজেক্টের তালিকার (অ্যারে) জন্য বিমূর্ত প্রকার। আসল বাস্তবায়নটি ছিল প্রাচীন .১.১ দিন, এবং এটি একটি এনপ্যাপুলেটেড অ্যারেলিস্ট ব্যবহার করেছে। আমরা সম্প্রতি http://blogs.msdn.com/b/joshwil/archive/2004/04/13/112598.aspx এ আবিষ্কার করেছি যে জেনেরিক তালিকা (তালিকা <VI>) মান ধরণের (যেমন আমাদের মত) অ্যারেলিস্টের তুলনায় অনেক ভাল সম্পাদন করে VI ম এনম) কারণ মানগুলি বাক্স করতে হবে না। এটি সত্য এবং এটি প্রায় কাজ করেছে।

সিএলআর প্রোফাইলার একটি বিস্ময় প্রকাশ করেছে। বরাদ্দ গ্রাফের একটি অংশ এখানে:

  • ভ্যালুআইডলিস্ট :: এতে বুল (ষষ্ঠ) 5.5 এমবি (34.81%) রয়েছে
  • জেনেরিক.লিস্ট :: বিলে রয়েছে (<ইউএনএনএনএন>) 5.5 এমবি (34.81%)
  • জেনেরিক.অবজেক্টএকোয়ালিটি কম্পাটার <টি> :: সমান বুল (<দুনিয়া << জ্ঞান>) 5.5 এমবি (34.88%)
  • মানসমূহ VI.VI.M এমবি (৪৯.০৩%)

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

আমাদের সমাধানটি কন্টেন্টগুলি () বাস্তবায়ন পুনরায় লেখার ছিল, যা আমাদের ক্ষেত্রে এটি করা সহজ ছিল যেহেতু আমরা ইতিমধ্যে জেনেরিক তালিকার অবজেক্ট (_items) এনপ্যাপুলেট করছি। এখানে সহজ কোড:

public bool Contains(VI id) 
{
  return IndexOf(id) >= 0;
}

public int IndexOf(VI id) 
{ 
  int i, count;

  count = _items.Count;
  for (i = 0; i < count; i++)
    if (_items[i] == id)
      return i;
  return -1;
}

public bool Remove(VI id) 
{
  int i;

  i = IndexOf(id);
  if (i < 0)
    return false;
  _items.RemoveAt(i);

  return true;
}

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


টিপটির জন্য ধন্যবাদ, আমি নিজেই খারাপ বক্সিং পারফরম্যান্সের দ্বারা ধরা পড়ছিলাম। একটি কাস্টম Containsবাস্তবায়ন আমার ব্যবহারের ক্ষেত্রে দ্রুততর।
লায়া হেইস

5

অভিধানটি ততটা খারাপ নয়, কারণ অভিধানের কীগুলি দ্রুত খুঁজে পাওয়ার জন্য ডিজাইন করা হয়েছে। একটি তালিকাতে একটি সংখ্যা খুঁজতে এটি পুরো তালিকার মাধ্যমে পুনরাবৃত্তি হওয়া দরকার।

অবশ্যই অভিধানগুলি কেবল তখনই কাজ করে যদি আপনার নম্বরগুলি অনন্য এবং অর্ডার না হয়।

আমি মনে করি HashSet<T>। নেট 3.5 এও একটি বর্গ রয়েছে, এটি কেবল অনন্য উপাদানগুলিকেই অনুমতি দেয়।


একটি অভিধান <টাইপ, পূর্ণসংখ্যা> কার্যকরভাবে অ-অনন্য বস্তুগুলিকেও কার্যকরভাবে সঞ্চয় করতে পারে - নকলের সংখ্যা গণনা করতে পূর্ণসংখ্যা ব্যবহার করুন। উদাহরণস্বরূপ, আপনি তালিকাটি {a, b, a} হিসাবে {a = 2, b = 1 store সঞ্চয় করে রাখবেন} এটি অবশ্যই অর্ডিংকে হারাবে।
এমসালটাররা


2

এটি আপনার প্রশ্নের ঠিক উত্তর নয়, তবে আমার একটি ক্লাস রয়েছে যা একটি সংকলনে কনটেনস () এর কার্যকারিতা বাড়িয়ে তোলে। আমি একটি সারি সাবক্লাস করেছি এবং একটি অভিধান যুক্ত করেছি যা বস্তুর তালিকায় হ্যাশকোড ম্যাপ করে। Dictionary.Contains()ফাংশন হে (1) যেহেতু List.Contains(), Queue.Contains()এবং Stack.Contains()হে (ঢ) হয়।

অভিধানের মান-প্রকার হ'ল একই হ্যাশকোডযুক্ত একটি সারি ধারণ করে objects কলকারী একটি কাস্টম শ্রেণীর অবজেক্ট সরবরাহ করতে পারে যা আইকুয়ালিটি কম্পিউটারের প্রয়োগ করে। আপনি স্ট্যাকস বা তালিকার জন্য এই প্যাটার্নটি ব্যবহার করতে পারেন। কোডটির জন্য কেবল কয়েকটি পরিবর্তন প্রয়োজন।

/// <summary>
/// This is a class that mimics a queue, except the Contains() operation is O(1) rather     than O(n) thanks to an internal dictionary.
/// The dictionary remembers the hashcodes of the items that have been enqueued and dequeued.
/// Hashcode collisions are stored in a queue to maintain FIFO order.
/// </summary>
/// <typeparam name="T"></typeparam>
private class HashQueue<T> : Queue<T>
{
    private readonly IEqualityComparer<T> _comp;
    public readonly Dictionary<int, Queue<T>> _hashes; //_hashes.Count doesn't always equal base.Count (due to collisions)

    public HashQueue(IEqualityComparer<T> comp = null) : base()
    {
        this._comp = comp;
        this._hashes = new Dictionary<int, Queue<T>>();
    }

    public HashQueue(int capacity, IEqualityComparer<T> comp = null) : base(capacity)
    {
        this._comp = comp;
        this._hashes = new Dictionary<int, Queue<T>>(capacity);
    }

    public HashQueue(IEnumerable<T> collection, IEqualityComparer<T> comp = null) :     base(collection)
    {
        this._comp = comp;

        this._hashes = new Dictionary<int, Queue<T>>(base.Count);
        foreach (var item in collection)
        {
            this.EnqueueDictionary(item);
        }
    }

    public new void Enqueue(T item)
    {
        base.Enqueue(item); //add to queue
        this.EnqueueDictionary(item);
    }

    private void EnqueueDictionary(T item)
    {
        int hash = this._comp == null ? item.GetHashCode() :     this._comp.GetHashCode(item);
        Queue<T> temp;
        if (!this._hashes.TryGetValue(hash, out temp))
        {
            temp = new Queue<T>();
            this._hashes.Add(hash, temp);
        }
        temp.Enqueue(item);
    }

    public new T Dequeue()
    {
        T result = base.Dequeue(); //remove from queue

        int hash = this._comp == null ? result.GetHashCode() : this._comp.GetHashCode(result);
        Queue<T> temp;
        if (this._hashes.TryGetValue(hash, out temp))
        {
            temp.Dequeue();
            if (temp.Count == 0)
                this._hashes.Remove(hash);
        }

        return result;
    }

    public new bool Contains(T item)
    { //This is O(1), whereas Queue.Contains is (n)
        int hash = this._comp == null ? item.GetHashCode() : this._comp.GetHashCode(item);
        return this._hashes.ContainsKey(hash);
    }

    public new void Clear()
    {
        foreach (var item in this._hashes.Values)
            item.Clear(); //clear collision lists

        this._hashes.Clear(); //clear dictionary

        base.Clear(); //clear queue
    }
}

আমার সহজ পরীক্ষামূলক শো যে আমার HashQueue.Contains()চেয়ে অনেক দ্রুত রান Queue.Contains()। 10,000 এ গণনা সহ পরীক্ষার কোডটি চালাতে হাশকিউ সংস্করণটির জন্য 0.00045 সেকেন্ড এবং ক্যু সংস্করণের জন্য 0.37 সেকেন্ড সময় লাগে। ১০,০০,০০০ গণনা সহ, হাশকিউ সংস্করণটি 0.0031 সেকেন্ড সময় নেয় যখন কাতারে লাগে 36.38 সেকেন্ড!

আমার পরীক্ষার কোডটি এখানে:

static void Main(string[] args)
{
    int count = 10000;

    { //HashQueue
        var q = new HashQueue<int>(count);

        for (int i = 0; i < count; i++) //load queue (not timed)
            q.Enqueue(i);

        System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            bool contains = q.Contains(i);
        }
        sw.Stop();
        Console.WriteLine(string.Format("HashQueue, {0}", sw.Elapsed));
    }

    { //Queue
        var q = new Queue<int>(count);

        for (int i = 0; i < count; i++) //load queue (not timed)
            q.Enqueue(i);

        System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            bool contains = q.Contains(i);
        }
        sw.Stop();
        Console.WriteLine(string.Format("Queue,     {0}", sw.Elapsed));
    }

    Console.ReadLine();
}

আমি কেবলমাত্র হ্যাশসেট <টি> এর জন্য তৃতীয় পরীক্ষার কেস যুক্ত করেছি যা আপনার সমাধানের চেয়ে আরও ভাল ফলাফল পেয়েছে বলে মনে হচ্ছে: HashQueue, 00:00:00.0004029 Queue, 00:00:00.3901439 HashSet, 00:00:00.0001716
গীতসংহিতা

1

কেন একটি অভিধান অনুপযুক্ত?

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


0

আমি এটি কমপ্যাক্ট ফ্রেমওয়ার্কে ব্যবহার করছি যেখানে হ্যাশসেটের জন্য কোনও সমর্থন নেই, আমি এমন একটি অভিধান বেছে নিয়েছি যেখানে উভয় স্ট্রিংই আমি যে মূল্যটি সন্ধান করছি।

এর অর্থ আমি অভিধানের পারফরম্যান্সের সাথে তালিকা <> কার্যকারিতা পেয়েছি। এটি কিছুটা হ্যাকি, তবে এটি কার্যকর।


4
যদি আপনি একটি হ্যাশসেটের পরিবর্তে অভিধান ব্যবহার করছেন তবে আপনি কীটির সাথে একই স্ট্রিংয়ের পরিবর্তে মানটি সেট করতে পারেন। এইভাবে আপনি কম স্মৃতি ব্যবহার করবেন। বিকল্পভাবে আপনি এমনকি অভিধান <স্ট্রিং, বুল> ব্যবহার করতে পারেন এবং সেগুলি সত্য (বা মিথ্যা) এ সেট করতে পারেন। আমি জানি না কোনটি কম স্মৃতি, একটি খালি স্ট্রিং বা বুল ব্যবহার করবে। আমার অনুমান বুল হবে।
টিটিটি

অভিধানে, একটি stringরেফারেন্স এবং boolমান যথাক্রমে 32 বা 64 বিট সিস্টেমের জন্য 3 বা 7 বাইটের পার্থক্য করে। তবে নোট করুন, প্রতিটি প্রবেশের আকারটি যথাক্রমে 4 বা 8 এর গুণক পর্যন্ত হয়। stringএবং এর মধ্যে পছন্দ boolএইভাবে আকারে কোনও পার্থক্য তৈরি করতে পারে না। খালি স্ট্রিংটি ""সর্বদা স্মৃতিতে স্থির সম্পত্তি হিসাবে সর্বদা বিদ্যমান থাকে string.Empty, তাই আপনি অভিধানে এটি ব্যবহার করেন বা না রাখুন তাতে কোনও তফাত আসে না। (এবং এটি অন্য যে কোনও জায়গায় ব্যবহৃত হয়))
ওয়ারম্বো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.