1

我想为 StringBuffer 实现一个比较器,它比较 StringBuffer 并相应地添加到 TreeSet。这纯粹是为了学习目的。我知道在 Hasable 集合中有一个可变对象是个坏主意。但这里的目的是如何为Java现有的StringBuffer类实现比较器并使用它来创建一个TreeSet。我当前的代码如下。代码无法编译。请帮帮我。谢谢

public class ComparatorExample {


    public class SbufferComparator implements Comparator<StringBuffer> {

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

        }

    }


        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>(new SbufferComparator());
                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);
            }
        }
4

4 回答 4

4

The inner class SbufferComparator must be static, or must be refactored to a top-level class.

Otherwise, it needs an enclosing object instance to be constructed:

ComparatorExample ce = new ComparatorExample();
SbufferComparator comparator = ce.new SbufferComparator();

Leaving it as a non-static inner class doesn't make much sense, since it doesn't use any instance field or method from its enclosing class. So make it static or top-level.

于 2013-08-22T21:51:49.813 回答
3

You should create the comparator class in a separate file not in the same class or make it static instead if you want to keep it inner.

    import java.util.Comparator;
    import java.util.Set;
    import java.util.TreeSet;

public class ComparatorExample {
private static class SbufferComparator implements Comparator<StringBuffer> {

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

        }

}


    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>(new SbufferComparator());
            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);
        }
    }

Pay attention to the import statements!

于 2013-08-22T21:51:57.990 回答
1

出于几个原因,比较或存储缓冲区是危险的。

  1. StringBuffer是不必要的线程安全的,并且基本上不赞成使用StringBuilder. 改用那个类。
  2. StringBuffer和都是StringBuilder可变的,这意味着它们不能安全地插入到 aTreeSet中,因为它们可能会被更改(例如,sb.insert(0, 'z')会使字符串以 开头z,从而更改任何连续比较的结果)并因此破坏TreeSet.
  3. 存储这样一个对象几乎没有什么好处,StringBuilder如果您需要继续使用它,稍后再构造一个新对象是微不足道的。
  4. 您的比较器很慢,每次调用比较器时都需要重建字符串,这就是O(m log n)缓冲区m长度和n树大小。

我强烈建议直接将字符串存储在您的文件中TreeSet。这将工作得更干净,更快,并避免危险的边缘情况,如渲染TreeSet乱序。


这是一个示例,它演示了如何在 a 中使用像缓冲区这样的可变对象来TreeSet打破所有期望:

public static void main(String[] args) {
    TreeSet<StringBuilder> tree = new TreeSet<>(new Comparator<StringBuilder>() {
        @Override
        public int compare(StringBuilder one, StringBuilder two) {
            return one.toString().compareTo(two.toString());
        }});

    char from = 'a', to = 'm'; // change to adjust map size
    char holdChar = 'd'; // change holdChar to adjust error location
    StringBuilder hold = null;

    for(char c = from; c <= to; c++) {
        StringBuilder sb = new StringBuilder().append(c).append(c).append(c);
        tree.add(sb);
        if(c == holdChar) {
            hold = sb;
        }
    }
    System.out.println(tree);

    hold.insert(0, to);

    for(char c = from; c <= to; c++) {
        StringBuilder sb = new StringBuilder().append(c).append(c).append(c);
        if(c == holdChar) {
            sb.insert(0, to);
        }
        System.out.println("Tree contains "+sb+(tree.contains(sb) ? "" : " NOPE!!"));
    }
    System.out.println(tree);
}

理论上,在第二个 for 循环中测试的每个 StringBuilder 都存在于 map 中,但 map 不再能够准确地确定这一点:

[aaa, bbb, ccc, ddd, eee, fff, ggg, hhh, iii, jjj, kkk, lll, mmm]
Tree contains aaa
Tree contains bbb
Tree contains ccc
Tree contains mddd
Tree contains eee NOPE!!
Tree contains fff NOPE!!
Tree contains ggg NOPE!!
Tree contains hhh NOPE!!
Tree contains iii NOPE!!
Tree contains jjj NOPE!!
Tree contains kkk NOPE!!
Tree contains lll NOPE!!
Tree contains mmm
[aaa, bbb, ccc, mddd, eee, fff, ggg, hhh, iii, jjj, kkk, lll, mmm]

更糟糕的是,由于底层的树结构,正确找到或未正确找到哪些字段取决于地图大小和错误点。玩弄from//值toholdChar你会看到任意不同的结果。举个holdChar = 'f';例子。

于 2013-08-22T22:07:03.433 回答
0

现在你有一个名为 SbufferComparator 的内部类。由于它是非静态的,因此您需要一个实例来访问其方法。你可以简单地把它变成静态的并且工作!

于 2013-08-22T23:32:58.357 回答