لماذا Long.valueOf (0) .equals (Integer.valueOf (0)) كاذبة؟

StackOverflow https://stackoverflow.com/questions/445990

  •  22-07-2019
  •  | 
  •  

سؤال

وتتم مطالبة هذه الأسئلة من خلال غريب السلوك HashMap.put ()

وأعتقد أنني أفهم لماذا يأخذ Map<K,V>.put على K لكن Map<K,V>.get يأخذ Object، على ما يبدو عدم القيام بذلك سوف كسر الكثير من التعليمات البرمجية الموجودة.

والآن نصل إلى سيناريو عرضة للخطأ جدا:

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L,"Five"); // compiler barfs on m.put(5, "Five")
m.contains(5); // no complains from compiler, but returns false

لا يمكن أن هذا قد تم حلها من خلال العودة صحيح إذا كانت قيمة Long كان WITHING مجموعة int والقيم متساوون؟

هل كانت مفيدة؟

المحلول

وهنا هو مصدر من Long.java

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

وأي بمعنى. فإنه يجب أن يكون نوع طويل لتكون على قدم المساواة. أعتقد أن الفرق الرئيسي بين:

long l = 42L
int i = 42;
l == i

وومثالك أعلاه هو أنه مع الأوليات يمكن أن يحدث اتساع ضمني من قيمة عدد صحيح، ولكن مع أنواع الكائنات لا توجد قواعد لتحويل ضمنيا من عدد صحيح إلى طويل.

وكما تحقق جافا الألغاز ، أن لديها الكثير من الأمثلة مشابهة لهذه.

نصائح أخرى

وبصفة عامة، على الرغم من أنه لا يعبر بدقة في العقد ل <لأ href = "http://java.sun.com/javase/6/docs/api/java/lang/Object.html#equals(java .lang.Object) "يختلط =" noreferrer "> يساوي () ، والأشياء لا ينبغي أن تعتبر نفسها مساوية لكائن آخر ليس من نفس الفئة الدقيق (حتى لو كان هو فئة فرعية). النظر في الملكية متماثل - إذا a.equals (ب) غير صحيح، ثم b.equals (أ) يجب أن يكون صحيحا أيضا

ودعونا كائنين، foo من Super الطبقة، وbar من Sub الطبقة التي تمتد Super. تنظر الآن تنفيذ equals() في السوبر، وتحديدا عندما دعا أنها foo.equals(bar). فو وحده يعلم أن شريط يتم كتابتها بقوة باعتبارها Object، وذلك للحصول على مقارنة دقيقة لا بد من التحقق من ذلك هو مثيل سوبر وإذا لم يكن عودة كاذبة. هو، ولذلك فإن هذا الجزء هو على ما يرام. فإنه يقارن الآن كافة الحقول سبيل المثال، وما إلى ذلك (أو أيا كان تنفيذ المقارنة الفعلي)، ويجد لهم على قدم المساواة. حتى الان جيدة جدا.

ولكن، في العقد يمكن أن يعود صحيحا إلا إذا كانت تعرف أن bar.equals (فو) هو الذهاب الى العودة الحقيقية أيضا. منذ شريط يمكن أن يكون أي فئة فرعية من سوبر، فإنه ليس من الواضح ما إذا كان يساوي () طريقة سوف يتم تجاوز (وإذا ربما سيكون). وبالتالي التأكد من أن التطبيق الخاص بك هو الصحيح، تحتاج إلى كتابتها بشكل متناظر والتأكد من أن الكائنات هما نفس الفئة.

والأهم من ذلك، لا يمكن حقا أن يعتبر أن الأجسام من فئات مختلفة على قدم المساواة - لأنه في هذه الحالة، واحدة منها فقط يمكن إدراجها في HashSet<Sub>، على سبيل المثال

نعم، ولكن كل ذلك يأتي نزولا إلى خوارزمية مقارنة ومدى لاتخاذ التحويلات. على سبيل المثال، ماذا تريد أن يحدث عند محاولة m.Contains("5")؟ أو إذا كنت تمر عليه مجموعة مع 5 باعتبارها العنصر الأول؟ يتحدث ببساطة، فإنه يبدو أن السلكية تصل "إذا كانت أنواع مختلفة، ومفاتيح مختلفة".

وبعد ذلك أخذت مجموعة مع object كمفتاح. ماذا تريد أن يحدث إذا كنت put على 5L، ثم محاولة للحصول على 5، "5"، ...؟ ماذا لو كنت put على 5L و5 و"5" وتريد أن تحقق ل5F؟

وبما أنه من مجموعة عام (أو قالب، أو ما كنت ترغب في الاتصال به)، فإنه سيتعين عليها أن تحقق والقيام ببعض خاص مقارنة لأنواع قيمة معينة. إذا K هو int ثم معرفة ما اذا كان الكائن الذي تم تمريره هو long، short، float، double، ...، ثم تحويل والمقارنة. إذا K هو float ثم معرفة ما اذا كان الكائن الذي تم تمريره هو ...

ويمكنك الحصول على هذه النقطة.

وتنفيذ آخر يمكن أن يكون لرمي استثناء إذا أنواع لم تطابق، ومع ذلك، وكثيرا ما كنت أتمنى أن يكون فعل.

وسؤالك يبدو معقولا على وجهها، ولكن سيكون انتهاكا لاتفاقيات العامة لليساوي ()، إن لم يكن عقدها، إلى العودة الحقيقية لنوعين مختلفين.

وكان جزء من تصميم لغة جافا لكائنات لتحويل أبدا ضمنا إلى أنواع أخرى، على عكس C ++. كان هذا جزءا من صنع جافا، لغة بسيطة صغيرة. جزء معقول من التعقيد C ++ الصورة تأتي من تحويلات ضمنية وتفاعلها مع غيرها من الميزات.

وأيضا، جافا لديه انفصام حاد وواضح بين الأوليات والكائنات. وهذا يختلف من لغات أخرى حيث يتم إخفاء هذا الاختلاف تحت الأغطية باعتبارها الأمثل. وهذا يعني أنه لا يمكن أن نتوقع طويل وصحيح أن يتصرف مثل طويلة وكثافة العمليات.

يمكن كتابة

ورمز مكتبة لإخفاء هذه الاختلافات، ولكن يمكن القيام به في الواقع الضرر بجعل بيئة برمجة أقل اتساقا.

وهكذا ينبغي أن يكون لك كود ....

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L, "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(5L)); // true

وأنت متناسين أن جافا وautoboxing التعليمات البرمجية الخاصة بك، لذلك سوف equivelenet رمز أعلاه إلى

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(new Long(5L), "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(new Long(5))); // true
System.out.println(m.containsKey(new Long(5L))); // true

وحتى جزء من مشكلتك هو autoboxing. أما الجزء الآخر هو أن لديك أنواع مختلفة كما ذكر غيرها من الملصقات.

والأجوبة أخرى تفسر بشكل كاف سبب فشلها، لكن أيا منها لا تتناول كيفية كتابة التعليمات البرمجية التي هي أقل عرضة للخطأ تغلب على هذه المشكلة. الحاجة إلى تذكر لإضافة نوع يلقي (أي مترجم مساعدة)، البدائيون لاحقة مع L وهكذا دواليك ليست مجرد IMHO مقبول.

وأنا أوصي باستخدام المكتبة الدفين GNU المجموعات عندما يكون لديك البدائيون (وفي العديد من الحالات الأخرى). على سبيل المثال، هناك TLongLongHashMap الذي يقوم بتخزين الأشياء interally يتوق كما البدائية. ونتيجة لذلك، لا يمكن ان ينتهي مع الملاكمة / علبته، وأبدا في نهاية المطاف مع السلوكيات غير متوقعة:

TLongLongHashMap map = new TLongLongHashMap();
map.put(1L, 45L);
map.containsKey(1); // returns true, 1 gets promoted to long from int by compiler
int x = map.get(1); // Helpful compiler error. x is not a long
int x = (int)map.get(1); // OK. cast reassures compiler that you know
long x = map.get(1); // Better.

ووهلم جرا. ليست هناك حاجة للحصول على نوع الحق، ومترجم يعطيك خطأ (أن تتمكن من تصحيح أو تجاوز) إذا كنت تفعل شيئا سخيفة (محاولة لتخزين لفترة طويلة في عدد صحيح).

وقواعد صناعة السيارات في صب يعني أن المقارنات تعمل بشكل صحيح أيضا:

if(map.get(1) == 45) // 1 promoted to long, 45 promoted to long...all is well

وعلى سبيل المكافأة، وذاكرة النفقات العامة وأداء وقت التشغيل هو أفضل بكثير.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top