могут ли объекты StringBuffer быть ключами в TreeSet в Java?

У меня есть следующий код, в котором я пытаюсь поместить объекты StringBuffer в качестве ключей в TreeSet. Причина, по которой я это делаю, состоит в том, чтобы посмотреть, могу ли я использовать изменяемые объекты в качестве ключей. Я не получаю никаких ошибок компиляции. но когда я запускаю этот код, я получаю сообщение об ошибке ниже кода. специально, я получаю это java.lang.StringBuffer cannot be cast to java.lang.Comparable. на что указывает эта ошибка?

из javadoc я вижу, что класс StringBuffer объявлен окончательным (public final class StringBuffer), не означает ли это, что он неизменяем и, следовательно, хэшируется?

Я новичок в хешировании и неизменяемых вещах, поэтому помогите мне здесь.

Спасибо

import java.util.*;
class MutableKeys {
public static void main(String[] args) {
        StringBuffer one = new StringBuffer("one");
        StringBuffer  two = new StringBuffer("two");
        StringBuffer three = new StringBuffer("three");
        Set<StringBuffer> sb=new TreeSet<StringBuffer>();
        sb.add(one);
        sb.add(two);
        sb.add(three);
        System.out.println("set before change: "+ sb);
        one.append("onemore");
        System.out.println("set After change: "+ sb);
    }
}

Exception in thread "main" java.lang.ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    at java.util.TreeMap.put(TreeMap.java:542)
    at java.util.TreeSet.add(TreeSet.java:238)
    at inheritance.MutableKeys.main

person eagertoLearn    schedule 22.08.2013    source источник


Ответы (8)


просто добавьте класс компаратора, а затем используйте его в своем TreeSet следующим образом:

class Comparatorbuff implements Comparator<StringBuffer> {

        @Override
        public int compare(StringBuffer s1, StringBuffer s2) {
            return s1.toString().compareTo(s2.toString());

        }

}

in your main method: modify as follows
Set<StringBuffer> sb=new TreeSet<StringBuffer>(new Comparatorbuff());
person brain storm    schedule 22.08.2013
comment
Если он изменяет StringBuffer и ищет его старое значение или новое значение никогда не будет найдено, потому что двоичный поиск будет индексироваться в позицию, где compareTo НИКОГДА не будет = 0; За исключением особого случая, когда изменение по-прежнему помещает значение в ту же относительную позицию дерева. Итог к плакату. Не изменяйте ключи. Очень очень плохо. Вы фактически теряете данные - person Christian Bongiorno; 23.08.2013

  1. Тот факт, что StringBuffer является public final class StringBuffer, означает, что вы не можете подклассифицировать его. StringBuffer довольно изменчив (в том-то и дело, что вы можете изменить содержимое буфера).

  2. Вы не хотите использовать что-то изменяемое в качестве ключа, потому что тогда после изменения объекта его методы equals() и hashcode() будут возвращать разные результаты, и вы больше не сможете найти его на карте.

  3. Если вы действительно хотите использовать StringBuffer в TreeSet, вам придется предоставить свой собственный Comparator, поскольку StringBuffer не реализует Comparable.

person dkatzel    schedule 22.08.2013

Проблема в том, что TreeSet сортирует предметы, которые вы в него помещаете. Поскольку StringBuffer не реализует Comparable, TreeSet не знает, как их сортировать. Вы должны передать Comparator при создании TreeSet. Ваш компаратор скажет TreeSet, как сортировать StringBuffer. Либо так, либо вы можете использовать HashSet, который не сортирует элементы.

Что касается неизменности: ключевое слово final в объявлении класса означает, что вы не можете подклассировать (расширить) его. Само по себе это не делает класс неизменным. Неизменяемость означает, что состояние объекта не может быть изменено после его создания. Состояние StringBuffer определенно может быть изменено после их создания, поэтому они не являются неизменяемыми.

person Eric Stein    schedule 22.08.2013

Объявление класса final не означает, что он неизменяемый, это означает, что ни один класс не может быть его подклассом. На самом деле StringBuffer очень изменчив; в этом суть класса.

Поскольку StringBuffer не Comparable, ваш TreeSet не знает, как сортировать ваш StringBuffers. Однако иметь изменяемый объект в качестве ключа в любом типе Set (или Map) — плохая идея. Если вам необходимо использовать TreeSet, создайте и используйте пользовательский объект Comparator, который сравнивает объекты StringBuffer.

person rgettman    schedule 22.08.2013

TreeSet принимает только Comparable объекты, где StringBuffer не является Comaprable объектом.

TreeSet#add

Throws-ClassCastException — если указанный объект нельзя сравнить с элементами, находящимися в данный момент в этом наборе.

Вы можете использовать объект String (поскольку строка сопоставима) вместо объекта StringBuffer.
Например:

    Set<String> sb=new TreeSet<String>();
    sb.add(one.toString());
    sb.add(two.toString());
    sb.add(three.toString());
    System.out.println("set before change: "+ sb);
    System.out.println("set After change: "+ sb);
person Prabhaker A    schedule 22.08.2013

Вы задаете несколько вопросов:

  1. общий вопрос: "можете ли вы иметь изменяемый ключ для хеша"
  2. Конкретный вопрос: «Можно ли использовать StringBuffer в качестве ключа для TreeSet»

Вы что-то напутали, и я помогу вам разобраться

Есть 2 стратегии идентификации, используемые с Картами в Java (более или менее).

  1. Хеширование: ввод «Foo» преобразуется в максимально возможную попытку сгенерировать число, которое однозначно обращается к индексу в массиве. (Пуристы, пожалуйста, не ругайте меня, я намеренно упрощаю). В этом индексе хранится ваше значение. Существует вероятность того, что «Foo» и «Bar» фактически генерируют одно и то же значение индекса, что означает, что они оба будут сопоставлены с одной и той же позицией массива. Очевидно, что это не может работать, и вот тут-то и появляется метод «equals()»; он используется для устранения неоднозначности

  2. Сравнение: при использовании сравнительного метода вам не нужен этот дополнительный шаг устранения неоднозначности, потому что сравнение НИКОГДА не приводит к этой коллизии. Единственный ключ, которому соответствует "Foo", - это "Foo". Однако действительно хорошая идея заключается в том, чтобы определить «equals()» как compareTo() == 0; ради согласованности. Не требование.

Теперь к вашему общему вопросу:

  1. Может ли ключ к карте быть изменяемым. Ответ: Да, очень, очень плохо и глупо. Пример: Map.put(k,v); k.modifyInternalHash(); Map.get(k) = ноль; // здесь плохо
    На самом деле это происходит из-за небрежного хеширования. Хотя это может произойти со сравнительными картами, диагностировать проблему будет гораздо проще.

  2. Можно ли использовать StringBuffer в качестве ключа для TreeMap/Set? Да. Используйте альтернативный конструктор: TreeSet(Comparator‹ T > comparator) и определите свой собственный метод сравнения для StringBuffer.

Удачи

person Christian Bongiorno    schedule 22.08.2013
comment
Означает ли это, что если я реализую интерфейс Comparable, то мне не нужно переопределять методы equals и hash, и все же я могу использовать свои объекты в HashMap? - person eagertoLearn; 23.08.2013
comment
Нет. Если ваш объект реализует Comparable, то он предназначен для работы с TreeSet/TreeMap или любым другим Sorted Set/Map. public int hashCode() и public boolean equals(Object o) предназначены специально для коллекции хеширования. Итак, шпаргалка: Использование HashSet или HashMap: необходимо реализовать hashCode() и equals() Использование TreeSet или TreeMap: необходимо реализовать Comparable или Comparator и передать конструктору - person Christian Bongiorno; 23.08.2013

Да, вы можете, но, как указано в приведенных выше ответах, вы должны написать Компаратор.

Но главный вопрос в том, зачем вам это? Целью StringBuffer является изменение состояния при создании строки. Поскольку это ключ в вашей SortedMap, вам не следует изменять ключ, поэтому нет смысла сохранять StringBuffer. Что вы хотите сделать, так это вызвать StringBuffer.toString(), который возвращает строку и использует строку в качестве вашего ключа.

person user2679791    schedule 22.08.2013

Мы зависим от обязательного естественного порядка сортировки по умолчанию, объект должен быть однородным и сопоставимым, иначе мы получим исключение времени выполнения, говорящее об исключении приведения класса. Объект считается сопоставимым тогда и только тогда, когда соответствующий класс реализует сопоставимый интерфейс. Класс String и все классы-оболочки уже реализуют сопоставимый интерфейс, но класс String Buffer не реализует сопоставимый интерфейс. Следовательно, это даст исключение стоимости класса для приведенного выше кода.

person Ankit Tripathi    schedule 12.12.2019