hashCode()
সংগ্রহের জন্য পদ্ধতির সর্বোত্তম প্রয়োগের বিষয়ে আমরা কীভাবে সিদ্ধান্ত নেব (ধরে নিই যে সমান পদ্ধতিটি সঠিকভাবে ওভাররাইড করা হয়েছে)?
collection.hashCode()
( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… )
hashCode()
সংগ্রহের জন্য পদ্ধতির সর্বোত্তম প্রয়োগের বিষয়ে আমরা কীভাবে সিদ্ধান্ত নেব (ধরে নিই যে সমান পদ্ধতিটি সঠিকভাবে ওভাররাইড করা হয়েছে)?
collection.hashCode()
( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… )
উত্তর:
সেরা বাস্তবায়ন? এটি একটি কঠিন প্রশ্ন কারণ এটি ব্যবহারের প্যাটার্নের উপর নির্ভর করে।
প্রায় সব ক্ষেত্রেই যুক্তিযুক্ত ভাল প্রয়োগের বিষয়টি আইটেম 8 (দ্বিতীয় সংস্করণ) এর জোশ ব্লচের কার্যকর জাভাতে প্রস্তাবিত হয়েছিল । সেখানে সন্ধান করা সবচেয়ে ভাল কারণ লেখক সেখানে ব্যাখ্যা করেছেন কেন পদ্ধতিরটি ভাল।
একটি তৈরি করুন int result
এবং একটি অ-শূন্য মান নির্ধারণ করুন ।
পদ্ধতিতে পরীক্ষিত প্রতিটি ক্ষেত্রের জন্য একটি হ্যাশ কোড গণনা করুন :f
equals()
c
boolean
: গণনা (f ? 0 : 1)
;byte
, char
, short
বা int
: ক্যালকুলেট (int)f
;long
: গণনা (int)(f ^ (f >>> 32))
;float
: গণনা Float.floatToIntBits(f)
;double
: Double.doubleToLongBits(f)
প্রতিটি দীর্ঘ মানের মতো রিটার্ন মান গণনা ও পরিচালনা করে;hashCode()
পদ্ধতির ফলাফল বা 0 যদি ব্যবহার করুন f == null
;এর c
সাথে হ্যাশ মানটি একত্রিত করুন result
:
result = 37 * result + c
প্রত্যাবর্তন result
এর ফলে বেশিরভাগ ব্যবহারের পরিস্থিতিতে হ্যাশ মানগুলির সঠিক বিতরণ করা উচিত।
আপনি যদি ডেমিস্টারের প্রস্তাবিত কার্যকর জাভা প্রয়োগে খুশি হন তবে আপনি নিজের ঘূর্ণায়মান পরিবর্তে একটি লাইব্রেরি কল ব্যবহার করতে পারেন:
@Override
public int hashCode() {
return Objects.hashCode(this.firstName, this.lastName);
}
এর জন্য com.google.common.base.Objects.hashCode
জাভা 7 ( java.util.Objects.hash
) এর মধ্যে পেয়ারা ( ) বা স্ট্যান্ডার্ড লাইব্রেরি প্রয়োজন তবে একইভাবে কাজ করে।
hashCode
যদি কাস্টম থাকে তবে আপনার কেবল ওভাররাইড করা উচিত equals
these সম্পর্কিত ডকুমেন্টেশন তাদের আচরণ সম্পর্কে বেশ স্পষ্ট equals
। একটি লাইব্রেরি বাস্তবায়ন আপনাকে সঠিক hashCode
বাস্তবায়নের বৈশিষ্ট্যগুলি কী তা জেনে বাধা দেওয়ার দাবি করে না - এই লাইব্রেরিগুলি আপনার ওভাররেড করা বেশিরভাগ ক্ষেত্রেই এই জাতীয় অনুসারী বাস্তবায়ন কার্যকর করে তোলেequals
।
java.util.Objects.hash(...)
পেয়ারা com.google.common.base.Objects.hashCode(...)
পদ্ধতির চেয়ে জেডিকে 7 পদ্ধতিটি বেছে নেব । আমি মনে করি বেশিরভাগ লোকেরা অতিরিক্ত নির্ভরতার চেয়ে মানক গ্রন্থাগারটি বেছে নেবে।
hashCode()
অ্যারেটির জন্য এটি কেবল তার java.lang.System.identityHashCode(...)
।
Eclipse দ্বারা সরবরাহিত কার্যকারিতাটি ব্যবহার করা আরও ভাল যা একটি খুব ভাল কাজ করে এবং আপনি ব্যবসায়ের যুক্তি বিকাশে আপনার প্রচেষ্টা এবং শক্তি রাখতে পারেন।
যদিও এটি Android
ডকুমেন্টেশন (ওয়েব্যাক মেশিন) এবং গিথুবে আমার নিজস্ব কোডের সাথে লিঙ্কযুক্ত , এটি সাধারণভাবে জাভার পক্ষে কাজ করবে। আমার উত্তরটি কেবল কোড সহ dmeister এর উত্তরের একটি বর্ধিতাংশ যা পড়তে এবং বুঝতে খুব সহজ।
@Override
public int hashCode() {
// Start with a non-zero constant. Prime is preferred
int result = 17;
// Include a hash for each field.
// Primatives
result = 31 * result + (booleanField ? 1 : 0); // 1 bit » 32-bit
result = 31 * result + byteField; // 8 bits » 32-bit
result = 31 * result + charField; // 16 bits » 32-bit
result = 31 * result + shortField; // 16 bits » 32-bit
result = 31 * result + intField; // 32 bits » 32-bit
result = 31 * result + (int)(longField ^ (longField >>> 32)); // 64 bits » 32-bit
result = 31 * result + Float.floatToIntBits(floatField); // 32 bits » 32-bit
long doubleFieldBits = Double.doubleToLongBits(doubleField); // 64 bits (double) » 64-bit (long) » 32-bit (int)
result = 31 * result + (int)(doubleFieldBits ^ (doubleFieldBits >>> 32));
// Objects
result = 31 * result + Arrays.hashCode(arrayField); // var bits » 32-bit
result = 31 * result + referenceField.hashCode(); // var bits » 32-bit (non-nullable)
result = 31 * result + // var bits » 32-bit (nullable)
(nullableReferenceField == null
? 0
: nullableReferenceField.hashCode());
return result;
}
সম্পাদনা
সাধারণত, আপনি যখন ওভাররাইড করবেন তখন আপনি ওভাররাইড hashcode(...)
করতে চান equals(...)
। সুতরাং যেগুলি ইতিমধ্যে বাস্তবায়ন করবে বা বাস্তবায়ন করেছে তাদের জন্য equals
এখানে আমার গিতুব থেকে একটি ভাল রেফারেন্স দেওয়া হয়েছে ...
@Override
public boolean equals(Object o) {
// Optimization (not required).
if (this == o) {
return true;
}
// Return false if the other object has the wrong type, interface, or is null.
if (!(o instanceof MyType)) {
return false;
}
MyType lhs = (MyType) o; // lhs means "left hand side"
// Primitive fields
return booleanField == lhs.booleanField
&& byteField == lhs.byteField
&& charField == lhs.charField
&& shortField == lhs.shortField
&& intField == lhs.intField
&& longField == lhs.longField
&& floatField == lhs.floatField
&& doubleField == lhs.doubleField
// Arrays
&& Arrays.equals(arrayField, lhs.arrayField)
// Objects
&& referenceField.equals(lhs.referenceField)
&& (nullableReferenceField == null
? lhs.nullableReferenceField == null
: nullableReferenceField.equals(lhs.nullableReferenceField));
}
প্রথমে নিশ্চিত হয়ে নিন যে সমানগুলি সঠিকভাবে প্রয়োগ করা হয়েছে। থেকে একটি IBM DeveloperWorks নিবন্ধ :
- প্রতিসামগ্রী: দুটি রেফারেন্সের জন্য, a এবং b, a.equals (b) এবং কেবলমাত্র b.equals (a)
- রিফ্লেক্সিভিটি: সমস্ত নাল রেফারেন্সের জন্য, a.equals (a)
- ট্রানজিটিভিটি: যদি a.equals (b) এবং b.equals (c) হয়, তবে a.equals (c)
তারপরে নিশ্চিত হয়ে নিন যে হ্যাশকোডের সাথে তাদের সম্পর্ক যোগাযোগটিকে সম্মান করে (একই নিবন্ধ থেকে):
- হ্যাশকোড () সহ সামঞ্জস্যতা: দুটি সমান বস্তুর অবশ্যই হ্যাশকোড () মান থাকতে হবে
পরিশেষে একটি ভাল হ্যাশ ফাংশন আদর্শ হ্যাশ ফাংশন যোগাযোগ করার চেষ্টা করা উচিত ।
সম্পর্কে 8.blogspot.com, আপনি বলেছেন
যদি সমান () দুটি বস্তুর জন্য সত্য দেয়, তবে হ্যাশকোড () একই মানটি প্রদান করবে। যদি সমান () মিথ্যা ফেরত দেয় তবে হ্যাশকোড () এর বিভিন্ন মান দেওয়া উচিত
আমি আপনার সাথে একমত হতে পারে না। দুটি বস্তুর যদি একই হ্যাশকোড থাকে তবে এর অর্থ এই নয় যে তারা সমান।
যদি A সমান B হয় তবে আহাশকোড অবশ্যই বি.স্কোডের সমান
কিন্তু
যদি এ্যাশকোড বি.এইচস্কোডের সমান হয় তবে এর অর্থ এই নয় যে এ অবশ্যই বি এর সমান হবে
(A != B) and (A.hashcode() == B.hashcode())
, আমরা একে হ্যাশ ফাংশন সংঘর্ষ বলি। এর কারণ হ্যাশ ফাংশনের কোডোমেন সর্বদা সসীম হয়, তবে এটির ডোমেন সাধারণত হয় না। কোডোমেন যত বড়, সংঘর্ষ কম হওয়া উচিত। ভাল হ্যাশ ফাংশনটি বিশেষ কোডোমেন আকারে সর্বাধিক সম্ভাব্য অর্জনের সাথে বিভিন্ন অবজেক্টের জন্য বিভিন্ন হ্যাশ ফিরিয়ে দেয়। এটি খুব কমই যদিও সম্পূর্ণরূপে গ্যারান্টিযুক্ত হতে পারে।
আপনি যদি গ্রহনটি ব্যবহার করেন তবে আপনি উত্পাদন equals()
এবং hashCode()
ব্যবহার করতে পারেন :
উত্স -> হ্যাশকোড তৈরি করুন () এবং সমান ()।
এই ফাংশনটি ব্যবহার করে আপনি সিদ্ধান্ত নিতে পারেন আপনি কোন ক্ষেত্রগুলি সাম্যতা এবং হ্যাশ কোড গণনার জন্য ব্যবহার করতে চান এবং Eclipse সম্পর্কিত পদ্ধতি উত্পন্ন করে।
সেখানে একটি ভাল বাস্তবায়ন এর কার্যকরী জাভা এর hashcode()
এবং equals()
যুক্তি এ্যাপাচি কমন্স ল্যাঙ । চেকআউট হ্যাশকোডবিল্ডার এবং সমতুল্য বিল্ডার ।
Objects
বর্গ উপলব্ধ hash(Object ..args)
& equals()
উপর Java7 থেকে পদ্ধতি। এই JDK 1.7+ ব্যবহার করে যে কোনও অ্যাপ্লিকেশনের জন্য সুপারিশ করা হয়
IdentityHashMap
)। FWIW আমি একটি আইডি-ভিত্তিক হ্যাশকোড ব্যবহার করি এবং সমস্ত সত্ত্বার সমান।
অন্যান্য আরও বিস্তারিত উত্তর (কোডের মেয়াদে) সম্পূর্ণ করার জন্য একটি দ্রুত নোট:
আমি যদি জাভা-তে-তৈরি-একটি-হ্যাশ-টেবিল-ইন-জাভা এবং বিশেষত জেগুরু এফএকিউ প্রবেশের প্রশ্নটি বিবেচনা করি তবে আমি বিশ্বাস করি যে হ্যাশ কোডটি বিচার করা যেতে পারে এমন আরও কিছু মানদণ্ড হল:
যদি আমি আপনার প্রশ্নটি সঠিকভাবে বুঝতে পারি তবে আপনার একটি কাস্টম সংগ্রহের ক্লাস রয়েছে (অর্থাত্ একটি নতুন শ্রেণি যা সংগ্রহ ইন্টারফেস থেকে প্রসারিত) এবং আপনি হ্যাশকোড () পদ্ধতিটি প্রয়োগ করতে চান।
যদি আপনার সংগ্রহ শ্রেণিটি অ্যাবস্ট্রাকলিস্টকে প্রসারিত করে, তবে আপনাকে এটি নিয়ে চিন্তিত হওয়ার দরকার নেই, ইতিমধ্যে সমান () এবং হ্যাশকোড () এর একটি বাস্তবায়ন রয়েছে যা সমস্ত বস্তুর মাধ্যমে পুনরাবৃত্তি করে এবং তাদের হ্যাশকোডগুলি () একসাথে যুক্ত করে কাজ করে।
public int hashCode() {
int hashCode = 1;
Iterator i = iterator();
while (i.hasNext()) {
Object obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
এখন আপনি যদি চান তবে নির্দিষ্ট শ্রেণীর জন্য হ্যাশ কোড গণনা করার সবচেয়ে ভাল উপায়, আমি সাধারণত আমি সমান পদ্ধতিতে ব্যবহার করি এমন সমস্ত ক্ষেত্রগুলি প্রক্রিয়া করতে ^ (বিটওয়াইজ এক্সক্লুসিভ বা) অপারেটরটি ব্যবহার করি:
public int hashCode(){
return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}
@ About8: সেখানে একটি দুর্দান্ত গুরুতর বাগ আছে।
Zam obj1 = new Zam("foo", "bar", "baz");
Zam obj2 = new Zam("fo", "obar", "baz");
একই হ্যাশকোড
আপনি সম্ভবত কিছু চান
public int hashCode() {
return (getFoo().hashCode() + getBar().hashCode()).toString().hashCode();
(আপনি কি আজকাল জাভাতে সরাসরি ইন্টার থেকে হ্যাশকোড পেতে পারেন? আমার মনে হয় এটি কিছুটা স্ব-কাস্টিং করে .. এটি যদি হয় তবে টসস্ট্রিং এড়িয়ে যান, এটি কুৎসিত))
foo
এবং bar
একই দিকে নিয়ে যায় hashCode
। আপনার toString
আফাইক সংকলন করে না, এবং যদি এটি হয় তবে এটি মারাত্মক অদক্ষ। এর মতো 109 * getFoo().hashCode() + 57 * getBar().hashCode()
কিছু দ্রুত, সহজ এবং অযথা সংঘাতের জন্ম দেয়।
অ্যাপাচি কমন্স ইকুয়ালস বিল্ডার এবং হ্যাশকোডবিল্ডারে প্রতিবিম্বের পদ্ধতিগুলি ব্যবহার করুন ।
আমি চারপাশে একটি ছোট্ট মোড়ক ব্যবহার করি Arrays.deepHashCode(...)
কারণ এটি প্যারামিটার হিসাবে সরবরাহ করা অ্যারেগুলি সঠিকভাবে পরিচালনা করে
public static int hash(final Object... objects) {
return Arrays.deepHashCode(objects);
}
যে কোনও হ্যাশিং পদ্ধতি যা সম্ভাব্য পরিসরের তুলনায় সমানভাবে হ্যাশ মান বিতরণ করে তা হ'ল একটি ভাল বাস্তবায়ন। কার্যকর জাভা দেখুন ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&res1Xi &res1X&res1X &&1 হ্যাশকোড বাস্তবায়নের জন্য সেখানে (আইটেম 9 আমি মনে করি ...)।
এখানে আরও একটি জেডিকে 1.7+ সুপারক্লাস লজিকসের সাথে যোগাযোগের বিক্ষোভ প্রদর্শন করা হয়েছে। আমি এটিকে অবজেক্ট ক্লাসের হ্যাশকোড () হিসাবে হিসাবরক্ষক, খাঁটি জেডিকে নির্ভরতা এবং কোনও অতিরিক্ত ম্যানুয়াল কাজের সাথে বেশ দৃ .়প্রতিজ্ঞ হিসাবে দেখছি। দয়া করে নোট Objects.hash()
নাল সহনশীল।
আমি কোনও equals()
বাস্তবায়ন অন্তর্ভুক্ত করি নি তবে বাস্তবে আপনার অবশ্যই এটির প্রয়োজন হবে।
import java.util.Objects;
public class Demo {
public static class A {
private final String param1;
public A(final String param1) {
this.param1 = param1;
}
@Override
public int hashCode() {
return Objects.hash(
super.hashCode(),
this.param1);
}
}
public static class B extends A {
private final String param2;
private final String param3;
public B(
final String param1,
final String param2,
final String param3) {
super(param1);
this.param2 = param2;
this.param3 = param3;
}
@Override
public final int hashCode() {
return Objects.hash(
super.hashCode(),
this.param2,
this.param3);
}
}
public static void main(String [] args) {
A a = new A("A");
B b = new B("A", "B", "C");
System.out.println("A: " + a.hashCode());
System.out.println("B: " + b.hashCode());
}
}
মান বাস্তবায়ন দুর্বল এবং এটি ব্যবহার করা অপ্রয়োজনীয় সংঘর্ষের দিকে পরিচালিত করে। ভাবুন ক
class ListPair {
List<Integer> first;
List<Integer> second;
ListPair(List<Integer> first, List<Integer> second) {
this.first = first;
this.second = second;
}
public int hashCode() {
return Objects.hashCode(first, second);
}
...
}
এখন,
new ListPair(List.of(a), List.of(b, c))
এবং
new ListPair(List.of(b), List.of(a, c))
একই আছে hashCode
যেমন, 31*(a+b) + c
জন্য ব্যবহার করা গুণক হিসাবে List.hashCode
এখানে পুনঃব্যবহৃত পায়। স্পষ্টতই, সংঘর্ষগুলি অনিবার্য, তবে অপ্রয়োজনীয় সংঘর্ষ উত্পাদন করা কেবল ... অযথা প্রয়োজন।
ব্যবহার সম্পর্কে যথেষ্ট স্মার্ট কিছুই নেই 31
। তথ্য হারাতে এড়াতে গুণককে অবশ্যই বিজোড় হতে হবে (যে কোনও গুণকও অন্তত সর্বাধিক উল্লেখযোগ্য বিট হারান, চারটি গুণক দুটি হারান ইত্যাদি), যে কোনও বিজোড় গুণক ব্যবহারযোগ্য। ছোট গুণকরা দ্রুত গণনার দিকে পরিচালিত করতে পারে (জেআইটি শিফট এবং সংযোজন ব্যবহার করতে পারে), তবে যে গুণটি আধুনিক ইন্টেল / এএমডি তে কেবল তিনটি চক্রের বিলম্বিত রয়েছে, এটি খুব কমই গুরুত্বপূর্ণ। ছোট গুণকগুলি ছোট ইনপুটগুলির জন্য আরও সংঘর্ষের দিকে পরিচালিত করে, যা কখনও কখনও সমস্যা হতে পারে।
প্রাইম ব্যবহার করা অর্থহীন কারণ প্রাইমগুলির জেড / (2 ** 32) রিংয়ের কোনও অর্থ নেই।
সুতরাং, আমি এলোমেলোভাবে নির্বাচিত বড় বিজোড় নম্বর ব্যবহার করার প্রস্তাব দিই (কোনও প্রাইম নিতে নিরবচ্ছিন্ন)। যেমন i86 / amd64 সিপিইউগুলি একক স্বাক্ষরিত বাইটে অপারেটিং ফিটিংয়ের জন্য একটি সংক্ষিপ্ত নির্দেশনা ব্যবহার করতে পারে, তাই 109 এর মতো গুণকগুলির জন্য একটি ছোট গতির সুবিধা রয়েছে coll সংঘর্ষ হ্রাস করতে, 0x58a54cf5 এর মতো কিছু নিন।
বিভিন্ন জায়গায় বিভিন্ন গুণক ব্যবহার করা সহায়ক, তবে অতিরিক্ত কাজটি ন্যায়সঙ্গত করার পক্ষে সম্ভবত যথেষ্ট নয়।
হ্যাশ মানগুলির সংমিশ্রণের সময়, আমি সাধারণত সম্মিলন পদ্ধতি ব্যবহার করি যা বুস্ট সি ++ লাইব্রেরিতে ব্যবহৃত হয়, যথা:
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
এটি একটি এমনকি বিতরণ নিশ্চিত করার জন্য মোটামুটি ভাল কাজ করে। এই সূত্রটি কীভাবে কাজ করে সে সম্পর্কে কিছু আলোচনার জন্য, স্ট্যাক ওভারফ্লো পোস্টটি দেখুন: জাদুতে জাদু সংখ্যা :: হ্যাশ_কোম্বাইন
এখানে বিভিন্ন হ্যাশ ফাংশন সম্পর্কে একটি ভাল আলোচনা রয়েছে: http://burtleburtle.net/bob/hash/doobs.html
একটি সাধারণ শ্রেণীর জন্য প্রায়শই সমান () প্রয়োগের মাধ্যমে পরীক্ষিত শ্রেণীর ক্ষেত্রগুলির উপর ভিত্তি করে হ্যাশকোড () প্রয়োগ করা সহজ।
public class Zam {
private String foo;
private String bar;
private String somethingElse;
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Zam otherObj = (Zam)obj;
if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
return true;
}
}
return false;
}
public int hashCode() {
return (getFoo() + getBar()).hashCode();
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
সর্বাধিক গুরুত্বপূর্ণ বিষয় হ্যাশকোড () এবং সমান () সামঞ্জস্য রাখা: যদি দুটি বস্তুর সমান () সমান হয়, তবে হ্যাশকোড () একই মানটি প্রদান করবে। যদি সমান () মিথ্যা ফেরত দেয় তবে হ্যাশকোড () এর বিভিন্ন মান প্রদান করা উচিত।
("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc")
। এটা মারাত্মক ত্রুটি। উভয় ক্ষেত্রের জন্য হ্যাশকোড মূল্যায়ন করা এবং তারপরে লিনিয়ার সংমিশ্রণ গণনা করা ভাল (অগ্রাধিকার সহগ হিসাবে প্রাইমগুলি ব্যবহার করা) ভাল would
foo
এবং bar
খুব অযথা সংঘাত উত্পাদন করে।
Objects.hashCode(collection)
একটি নিখুঁত সমাধান হওয়া উচিত!