如何在Java中为StringBuffer类实现比较器以便在TreeSet中使用?

时间:2013-08-22 21:46:04

标签: java comparator treeset stringbuffer

我想为StringBuffer实现一个Comparator,它比较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 个答案:

答案 0 :(得分:4)

内部类SbufferComparator必须是静态的,或者必须重构为顶级类。

否则,它需要构造一个封闭的对象实例:

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

将它作为非静态内部类没有多大意义,因为它不使用其封闭类中的任何实例字段或方法。所以让它静态或顶级。

答案 1 :(得分:3)

您应该在不在同一个类中的单独文件中创建比较器类,或者如果您想将其保持在内部,则将其设置为静态。

    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);
        }
    }

注意导入声明!

答案 2 :(得分:1)

由于某些原因,比较或存储缓冲区是危险的。

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

    我强烈建议您直接将字符串存储在TreeSet中。这样可以更清晰,更快速,并避免出现危险的边缘情况,例如使TreeSet无序。


    这是一个演示如何使用像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都存在于地图中,但地图不再能够准确地确定:

    [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 / to / holdChar值进行游戏,您会看到任意不同的结果。例如,尝试holdChar = 'f';

答案 3 :(得分:0)

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