হ্যাশম্যাপ জাভা 8 বাস্তবায়ন


94

নিম্নলিখিত লিঙ্ক নথি হিসাবে: জাভা হ্যাশম্যাপ বাস্তবায়ন

আমি বাস্তবায়নের সাথে বিভ্রান্ত HashMap(বা বরং, একটি বর্ধন HashMap)। আমার প্রশ্নগুলি হ'ল:

প্রথমত

static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;

কেন এবং কিভাবে এই ধ্রুবক ব্যবহার করা হয়? আমি এর জন্য কিছু সুস্পষ্ট উদাহরণ চাই। তারা কীভাবে এটি দিয়ে একটি পারফরম্যান্স লাভ অর্জন করছে?

দ্বিতীয়ত:

আপনি যদি HashMapজেডিকে উত্স কোডটি দেখতে পান তবে আপনি নীচের স্থির অভ্যন্তর শ্রেণিটি দেখতে পাবেন:

static final class TreeNode<K, V> extends java.util.LinkedHashMap.Entry<K, V> {
    HashMap.TreeNode<K, V> parent;
    HashMap.TreeNode<K, V> left;
    HashMap.TreeNode<K, V> right;
    HashMap.TreeNode<K, V> prev;
    boolean red;

    TreeNode(int arg0, K arg1, V arg2, HashMap.Node<K, V> arg3) {
        super(arg0, arg1, arg2, arg3);
    }

    final HashMap.TreeNode<K, V> root() {
        HashMap.TreeNode arg0 = this;

        while (true) {
            HashMap.TreeNode arg1 = arg0.parent;
            if (arg0.parent == null) {
                return arg0;
            }

            arg0 = arg1;
        }
    }
    //...
}

এটি কীভাবে ব্যবহৃত হয়? আমি কেবল অ্যালগরিদমের ব্যাখ্যা চাই

উত্তর:


229

HashMapবালতি একটি নির্দিষ্ট সংখ্যা রয়েছে। এটি hashCodeকোন বালতিটি toোকাতে হবে তা নির্ধারণ করতে এটি ব্যবহার করে। সরলতার জন্য এটি একটি মডুলাস হিসাবে কল্পনা করুন।

আমাদের হ্যাশকোডটি যদি 123456 হয় এবং আমাদের 4 টি বালতি থাকে, 123456 % 4 = 0তাই আইটেমটি প্রথম বালতিতে চলে যায়, বালতি 1।

হ্যাশ মানচিত্র

যদি আমাদের hashCodeফাংশনটি ভাল হয় তবে এটির একটি সমান বিতরণ করা উচিত যাতে সমস্ত বালতি কিছুটা সমানভাবে ব্যবহৃত হয়। এই ক্ষেত্রে, বালতি মানগুলি সংরক্ষণ করার জন্য একটি লিঙ্কযুক্ত তালিকা ব্যবহার করে।

লিঙ্কযুক্ত বালতি

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

খারাপ হ্যাশম্যাপ

এই বিতরণ যত কম হবে ততই আমরা ও (1) ক্রিয়াকলাপ থেকে আরও এগিয়ে চলেছি এবং আমরা O (n) ক্রিয়াকলাপের দিকে আরও এগিয়ে চলেছি।

হ্যাশম্যাপের প্রয়োগটি যদি বালতিগুলি বেশি বড় হয়ে যায় তবে লিঙ্কযুক্ত তালিকার চেয়ে কিছু বালতি গাছগুলিতে সংগঠিত করে এটিকে হ্রাস করার চেষ্টা করে। এই কি TREEIFY_THRESHOLD = 8জন্য হয়। যদি একটি বালতিতে আটটির বেশি আইটেম থাকে তবে এটি একটি গাছে পরিণত হওয়া উচিত।

গাছের বালতি

এই গাছটি একটি লাল-কালো গাছ , সম্ভবত এটি বেছে নেওয়া হয়েছে কারণ এটি কিছু খারাপ-গ্যারান্টি সরবরাহ করে। এটি প্রথম হ্যাশ কোড অনুসারে বাছাই করা হয়। যদি হ্যাশ কোডগুলি একই হয়, তবে বস্তুগুলি সেই ইন্টারফেসটি প্রয়োগ করে কিনা তা compareToপদ্ধতি ব্যবহার করে Comparable, অন্যথায় পরিচয় হ্যাশ কোড।

মানচিত্র থেকে এন্ট্রিগুলি সরিয়ে ফেলা হলে, বালতিতে প্রবেশের সংখ্যাগুলি হ্রাস করতে পারে যে এই গাছের কাঠামোর আর প্রয়োজন নেই। এটা কি UNTREEIFY_THRESHOLD = 6জন্য হয়। যদি একটি বালতিতে উপাদানের সংখ্যা ছয়টির নিচে নেমে যায়, তবে আমরা সম্ভবত একটি লিঙ্কযুক্ত তালিকা ব্যবহার করতে ফিরে যেতে পারি।

অবশেষে, আছে MIN_TREEIFY_CAPACITY = 64

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


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

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


16

এটিকে সরল রাখতে (যতই সহজ আমি যতটা সহজ করতে পারি) + আরও কিছু বিশদ।

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

TREEIFY_THRESHOLD -> যখন একক বালতি এটিতে পৌঁছায় (এবং মোট সংখ্যাটি ছাড়িয়ে যায় MIN_TREEIFY_CAPACITY), এটি একেবারে সুষম লাল / কালো গাছের নোডে রূপান্তরিত হয় । কেন? কারণ অনুসন্ধানের গতি। এটি সম্পর্কে অন্যভাবে চিন্তা করুন:

পূর্ণসংখ্যার সাথে বালতি / বিনের মধ্যে প্রবেশের সন্ধান করতে সর্বাধিক 32 টি পদক্ষেপ নিতে হবে MA MAX_VALUE এন্ট্রি।

পরবর্তী বিষয়ের জন্য কিছু ভূমিকা। বিন / বালতির সংখ্যা সর্বদা দু'জনের কেন? কমপক্ষে দুটি কারণ: মডিউলো অপারেশন থেকে দ্রুত এবং নেতিবাচক সংখ্যায় মডুলো নেতিবাচক হবে। এবং আপনি একটি "নেতিবাচক" বালতিতে কোনও এন্ট্রি রাখতে পারবেন না:

 int arrayIndex = hashCode % buckets; // will be negative

 buckets[arrayIndex] = Entry; // obviously will fail

পরিবর্তে মডিউলগুলির পরিবর্তে একটি দুর্দান্ত কৌশল ব্যবহৃত হয়েছে:

 (n - 1) & hash // n is the number of bins, hash - is the hash function of the key

এটি শব্দার্থগতভাবে মডুলো অপারেশন হিসাবে একই । এটি নিম্ন বিট রাখা হবে। এটি করার পরে একটি আকর্ষণীয় পরিণতি হয়:

Map<String, String> map = new HashMap<>();

উপরের ক্ষেত্রে, কেবলমাত্র আপনার হ্যাশকোডের শেষ 4 টি বিটের ভিত্তিতে কোনও প্রবেশ কোথায় হবে সে সিদ্ধান্ত নেওয়া হয়

এইখানেই বালতিগুলি গুন করা খেলায় আসে। নির্দিষ্ট শর্তের অধীনে ( সঠিক বিবরণে ব্যাখ্যা করতে অনেক সময় লাগবে ), বালতিগুলি আকারে দ্বিগুণ হয়ে যায়। কেন? যখন বালতিগুলি আকারে দ্বিগুণ হয়, তখন আরও একটি বিট খেলতে আসে

সুতরাং আপনার কাছে 16 টি বালতি রয়েছে - হ্যাশকোডের শেষ 4 টি বিট স্থির করে যে কোনও প্রবেশিকা কোথায়। আপনি বালতিগুলি দ্বিগুণ করেছেন: 32 বালতি - 5 টি সর্বশেষ বিট স্থির করে যেখানে প্রবেশ করবে।

এই প্রক্রিয়াটিকে রি-হ্যাশিং বলা হয়। এটি ধীর হতে পারে। এটি (যারা যত্নশীল তাদের পক্ষে) হ্যাশম্যাপ হিসাবে "রসিকতা" দেওয়া হয়েছে: দ্রুত, দ্রুত, দ্রুত, স্লোও । অন্যান্য বাস্তবায়ন রয়েছে - অনুসন্ধান বিরতিহীন হ্যাশম্যাপ ...

এখন UNTREEIFY_THRESHOLD পুনরায় হ্যাশ করার পরে খেলায় আসে। এই মুহুর্তে, কিছু এন্ট্রিগুলি এই বিনা থেকে অন্যের দিকে চলে যেতে পারে (তারা গণনায় আরও একটি বিট যোগ করে (n-1)&hash- এবং এটি অন্যান্য বালতিতে যেতে পারে ) এবং এটি এ পৌঁছতে পারে UNTREEIFY_THRESHOLD। এই মুহুর্তে এটি বিন হিসাবে রাখার জন্য অর্থ প্রদান করে না red-black tree node, তবে LinkedListপরিবর্তে পছন্দ করে

 entry.next.next....

একটি নির্দিষ্ট বালতি গাছের গাছে রূপান্তরিত হওয়ার আগে MIN_TREEIFY_CAPACITY হ'ল ন্যূনতম সংখ্যা।


10

TreeNodeএর একক বিনের সাথে সম্পর্কিত এন্ট্রিগুলি সংরক্ষণ করার একটি বিকল্প উপায় HashMap। পুরানো প্রয়োগসমূহে একটি বিনের এন্ট্রিগুলি একটি লিঙ্কযুক্ত তালিকায় সংরক্ষণ করা হয়েছিল। জাভা 8-তে, যদি একটি বিনের প্রবেশের সংখ্যা একটি প্রান্তিক ( TREEIFY_THRESHOLD) একটি পাস করে , তবে তারা মূল লিঙ্কযুক্ত তালিকার পরিবর্তে গাছের কাঠামোয় সংরক্ষণ করা হয়। এটি একটি অপ্টিমাইজেশন।

বাস্তবায়ন থেকে:

/*
 * Implementation notes.
 *
 * This map usually acts as a binned (bucketed) hash table, but
 * when bins get too large, they are transformed into bins of
 * TreeNodes, each structured similarly to those in
 * java.util.TreeMap. Most methods try to use normal bins, but
 * relay to TreeNode methods when applicable (simply by checking
 * instanceof a node).  Bins of TreeNodes may be traversed and
 * used like any others, but additionally support faster lookup
 * when overpopulated. However, since the vast majority of bins in
 * normal use are not overpopulated, checking for existence of
 * tree bins may be delayed in the course of table methods.

না ঠিক সত্য। যদি তারা পাস করে TREEIFY_THRESHOLD এবং মোট বিনের সংখ্যা কমপক্ষে MIN_TREEIFY_CAPACITY। আমি আমার উত্তরে এটি কভার করার চেষ্টা করেছি ...
ইউজিন

3

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

public class Key implements Comparable<Key>{

  private String name;

  public Key (String name){
    this.name = name;
  }

  @Override
  public int hashCode(){
    return 1;
  }

  public String keyName(){
    return this.name;
  }

  public int compareTo(Key key){
    //returns a +ve or -ve integer 
  }

}

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

Map<Key, String> map = new HashMap<>();

    Key key1 = new Key("key1");
    map.put(key1, "one");

    Key key2 = new Key("key2");
    map.put(key2, "two");
    Key key3 = new Key("key3");
    map.put(key3, "three");
    Key key4 = new Key("key4");
    map.put(key4, "four");
    Key key5 = new Key("key5");
    map.put(key5, "five");
    Key key6 = new Key("key6");
    map.put(key6, "six");
    Key key7 = new Key("key7");
    map.put(key7, "seven");
    Key key8 = new Key("key8");
    map.put(key8, "eight");

//Since hascode is same, all entries will land into same bucket, lets call it bucket 1. upto here all entries in bucket 1 will be arranged in LinkedList structure e.g. key1 -> key2-> key3 -> ...so on. but when I insert one more entry 

    Key key9 = new Key("key9");
    map.put(key9, "nine");

  threshold value of 8 will be reached and it will rearrange bucket1 entires into Tree (red-black) structure, replacing old linked list. e.g.

                  key1
                 /    \
               key2   key3
              /   \   /  \

লিংকডলিস্ট {ও (এন)} এর চেয়ে ট্রি ট্রভারসাল দ্রুত {O (লগ এন) is এবং n বাড়ার সাথে সাথে পার্থক্য আরও তাত্পর্যপূর্ণ হয়ে ওঠে।


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

@ মিম্বিস তাদের হ্যাশকোডগুলি অগত্যা একই নয়। তারা সম্ভবত অন্যরকম। ক্লাস তা বাস্তবায়ন হলে, এটি অতিরিক্ত ব্যবহার করবে compareToথেকে ComparableidentityHashCodeএটি ব্যবহার করে এমন আরেকটি প্রক্রিয়া।
মাইকেল

@ মিশেল এই উদাহরণে সমস্ত হ্যাশকোড অগত্যা একই এবং শ্রেণি তুলনামূলক প্রয়োগ করে না। পরিচয় হ্যাশকোড সঠিক নোড সন্ধানে মূল্যহীন হবে।
ব্যবহারকারী 253751

@ মিম্বিস আহা হ্যাঁ, আমি কেবল এটি স্কিম করেছিলাম তবে আপনি ঠিক বলেছেন। সুতরাং, Keyবাস্তবায়িত হয় না হিসাবে Comparable, identityHashCodeব্যবহার করা হবে :)
মাইকেল

@ ইমনমিশ্রা দুর্ভাগ্যবশত, সহজভাবে ভিজ্যুয়াল যথেষ্ট হবে না, আমি আমার উত্তরে এটি কভার করার চেষ্টা করেছি।
ইউজিন

2

হ্যাশম্যাপ বাস্তবায়নের পরিবর্তনটি জেইপি -180 যুক্ত করা হয়েছিল । উদ্দেশ্য ছিল:

মানচিত্রে প্রবেশের জন্য লিঙ্কযুক্ত তালিকার পরিবর্তে সুষম গাছ ব্যবহার করে উচ্চ হ্যাশ-সংঘর্ষের পরিস্থিতিতে java.util.HashMap এর কার্যকারিতা উন্নত করুন। লিংকডহ্যাশম্যাপ ক্লাসে একই উন্নতি বাস্তবায়ন করুন

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


-1

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

একটি সত্য হ্যাশ ফাংশন অবশ্যই এই বিধি অনুসরণ করবে -

“ফাংশনটি একই বা সমান বস্তুতে প্রয়োগ করার সময় হ্যাশ ফাংশনটি প্রতিটি এবং একই সময় হ্যাশ কোডটি ফেরত পাঠায়। অন্য কথায়, দুটি সমান বস্তুকে অবশ্যই একই হ্যাশ কোডটি ধারাবাহিকভাবে উত্পন্ন করতে হবে। "


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