Özel bir karşılaştırıcıyla bir TreeSet'ten kaldırmak neden daha büyük bir öğe kümesini kaldırmıyor?


22

Hem Java 8 hem de Java 11'i TreeSetkullanarak bir String::compareToIgnoreCasekarşılaştırıcı ile aşağıdakileri göz önünde bulundurun :

final Set<String> languages = new TreeSet<>(String::compareToIgnoreCase);
languages.add("java");
languages.add("c++");
languages.add("python");

System.out.println(languages);                 // [c++, java, python]

Ben mevcut kesin öğeleri kaldırmaya çalıştığınızda TreeSetçalışır: belirtilen tüm kaldırılır:

languages.removeAll(Arrays.asList("PYTHON", "C++"));

System.out.println(languages);                 // [java]

Ancak, yerine mevcut olandan daha fazla kaldırmaya çalışırsanız TreeSet, çağrı hiçbir şey kaldırmaz (bu sonraki bir çağrı değildir, ancak yukarıdaki snippet yerine çağrılır):

languages.removeAll(Arrays.asList("PYTHON", "C++", "LISP"));

System.out.println(languages);                 // [c++, java, python]

Neyi yanlış yapıyorum? Neden bu şekilde davranıyor?

Düzenle: String::compareToIgnoreCasegeçerli bir karşılaştırıcıdır:

(l, r) -> l.compareToIgnoreCase(r)

5
İlgili hata girişi: bugs.openjdk.java.net/browse/JDK-8180409 (TreeSet removeDize ile tutarsız tüm davranışlar.CASE_INSENSITIVE_ORDER)
Progman

Yakından ilgili bir Soru-Cevap .
Naman

Yanıtlar:


22

İşte Javadoc var removeAll () :

Bu uygulama, her biri için boyut yöntemini çağırarak bu kümeden ve belirtilen koleksiyondan hangisinin daha küçük olduğunu belirler. Bu kümede daha az öğe varsa, uygulama yineleyici tarafından döndürülen her öğeyi belirtilen koleksiyonda yer alıp almadığını kontrol ederek bu küme üzerinden yinelenir. Öyle içeriliyorsa, yineleyicinin kaldırma yöntemiyle bu kümeden kaldırılır. Belirtilen koleksiyonda daha az öğe varsa, uygulama belirtilen kümenin üzerinde yinelenerek bu kümeden kaldırılarak yineleyici tarafından döndürülen her öğe kaldırılır.

İkinci denemenizde, javadoc'un ilk örneğinde olursunuz. Böylece "java", "c ++" vb Set.of("PYTHON", "C++"). Onlar değil, bu yüzden kaldırılmazlar. Bağımsız değişkenle aynı karşılaştırıcıyı kullanarak başka bir TreeSet kullanın ve düzgün çalışması gerekir. Biri kullanan equals()diğeri karşılaştırıcı kullanan iki farklı Set uygulaması kullanmak gerçekten tehlikeli bir şeydir.

Bunun hakkında bir hata açıldığını unutmayın: [JDK-8180409] TreeSet removeAll.CASE_INSENSITIVE_ORDER ile tutarsız tüm davranışlar .


Her iki set de aynı özelliklere sahip olduğunda, yani işe yarıyor mu? final Set<String> subLanguages = new TreeSet<>(String::compareToIgnoreCase); subLanguages.addAll(Arrays.asList("PYTHON", "C++", "LISP")); languages.removeAll(subLanguages);
Nikolas

1
Javadoc tarafından tarif edilen "Bu sette daha az eleman varsa" söz konusu. Diğer durum "Belirtilen koleksiyonda daha az öğe varsa" olur.
JB Nizet

8
Bu cevap doğru, ama çok sezgisel olmayan bir davranış. Tasarımında bir kusur gibi hissediyor TreeSet.
Ocak'ta Boann

Katılıyorum, ama bu konuda hiçbir şey yapamam.
JB Nizet

4
Her ikisi de: doğru bir şekilde belgelenmiş çok sezgisel olmayan bir davranış, ancak sezgisel olmayan ve aldatıcı olmak, aynı zamanda bir gün düzeltilebilecek bir tasarım hatası.
JB Nizet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.