自定义比较器

时间:2017-06-01 06:47:32

标签: java list sorting comparator

给出以下列表:"A", "B", "C", "D", "E", "F", "G"

我需要一个进行以下排序的比较器:

  • 指定某个元素(例如"D"
  • 从元素开始
  • 后跟原始顺序中原始列表的所有后续元素
  • 后跟原始顺序中原始列表的所有前面元素

结果将是:"D", "E", "F", "G", "A", "B", "C"

请注意,我知道我可以做类似以下的事情:

List<String> following = myList.subList(myList.indexOf("D") + 1, myList.size());
List<String> preceding = myList.subList(0, myList.indexOf("D"));
List<String> newList = Stream.of(Collections.singletonList("D"), following, preceding)
                       .flatMap(List::stream)
                       .collect(Collectors.toList());

在此问题中,我明确指出Comparator实施。

很明显,它必须有列表&amp; element作为参数,我只是不清楚比较算法本身:

private static class MyComparator<T> implements Comparator<T> {

  private final List<T> list;
  private final T element;

  private MyComparator(List<T> list, T element) {
    this.list = list;
    this.element = element;
  }

  @Override
  public int compare(T o1, T o2) {
    // Not clear
  }
}

6 个答案:

答案 0 :(得分:1)

如果我问你这个问题Collections.sort(myList, myComparator);

然后我可以建议您使用定义的整理器:

List<String> myList = Arrays.asList("A", "B", "C", "D", "E", "F", "G");
System.out.println(myList);
String rules = "< d,D < e,E < f,F < g,G < a,A < b,B < c,C";

RuleBasedCollator ruleBasedCollator = new RuleBasedCollator(rules);
Collections.sort(myList, ruleBasedCollator);
System.out.println(myList);

此处的规则是"< d,D < e,E < f,F < g,G < a,A < b,B < c,C",这意味着哪些字符的权重高于其他字符...其余字符通常是排序方法

输出

  

[A,B,C,D,E,F,G]

     

[D,E,F,G,A,B,C]

答案 1 :(得分:1)

在这里使用Comparator没有多大意义,因为它会非常低效(因为你必须找到原始列表中2个比较元素的索引,这会导致每个比较需要线性时间)。

如果列表包含重复项,它可能会失败。

然而,既然你问过,这样的事可能有用:

public int compare(T o1, T o2) {
    if (o1.equals(o2)
        return true;
    int i1 = list.indexOf(o1);
    int i2 = list.indexOf(o2);
    int ie = list.indexOf(element); // this can be done in the constructor of the Comparator
    // now you have to check whether i1 and i2 are smaller than or larger than
    // ie, and based on that determine which of the corresponding elements should come
    // first
}

或者,只需致电

Collections.rotate(list,list.size() - list.indexOf(element));

答案 2 :(得分:1)

我认为这就是你想要的:

class ImposedOrder<T> implements Comparator<T> {

    private final List<T> list;
    private final int startIndex;

    ImposedOrder(List<T> list, T startElement) {
        this.list = new ArrayList<>(list);
        this.startIndex = list.indexOf(startElement);
        if (startIndex < 0) {
            throw new IllegalArgumentException();
        }
    }

    @Override
    public int compare(T t1, T t2) {
        int t1Index = list.indexOf(t1);
        int t2Index = list.indexOf(t2);
        return Integer.compare(adjust(t1Index), adjust(t2Index));
    }

    private int adjust(int rawIndex) {
        if (rawIndex >= startIndex) {
            return rawIndex;
        }
        return rawIndex + list.size();
    }
}

一些额外的验证可能是为了避免带有重复的强加订单列表。

使用indexOf进行线性搜索并不能提供出色的效果,但对于小型订单列表,它可能就足够了。否则,您可以在Comparator的构造函数中将元素映射到其调整后的索引,而不是保存强制订单列表的副本。

像这样:

class ImposedOrder<T> implements Comparator<T> {

    private final Map<T, Integer> map;
    private final int startIndex;

    ImposedOrder(List<T> list, T startElement) {
        this.startIndex = list.indexOf(startElement);
        if (startIndex < 0) {
            throw new IllegalArgumentException();
        }
        this.map = IntStream.range(0, list.size())
                .boxed()
                .collect(Collectors.toMap(
                        list::get,
                        i -> adjust(startIndex, list.size(), i)
                ));
    }

    @Override
    public int compare(T t1, T t2) {
        Integer t1Index = map.get(t1);
        Integer t2Index = map.get(t2);
        return t1Index.compareTo(t2Index);
    }

    private static int adjust(int startIndex, int size, int rawIndex) {
        if (rawIndex >= startIndex) {
            return rawIndex;
        }
        return rawIndex + size;
    }
}

答案 3 :(得分:0)

我知道这是可能的,但它很混乱...... 且不安全

将第一个块推到最后的想法是添加fn以强制这些值更大。这样,“A”比“G”大,如果它与“ZA”比较时。

String
  

[D,E,F,A,B,C]

这是不安全的,因为这可能会更复杂,前提是一个更大的安全值,我考虑使用 List<String> list = Arrays.asList(new String[]{ "A", "B", "C", "D", "E", "F"}); final String split = "D"; Collections.sort(list, new Comparator<String>(){ public int compare(String o1, String o2) { //Prepend with a big String value (like "ZZZZ") if it is before the value `split`. if(o1.compareTo(split) < 0) o1 = "ZZZZ" + o1; if(o2.compareTo(split) < 0) o2 = "ZZZZ" + o2; return o1.compareTo(o2); //then compare } }); System.out.println(list); ,但我不确定这会更安全。好吧,使用Character.MAX_VALUE值本身可能更简单..

答案 4 :(得分:0)

您需要比较元素的索引。

private static class MyComparator<T> implements Comparator<T> {

    private final List<T> list;
    private final int elementIndex;

    private MyComparator(List<T> list, T element) {
        // keep original order
        this.list = new ArrayList<>(list);
        this.elementIndex = list.indexOf(element);
    }

    @Override
    public int compare(T o1, T o2) {
        int i1 = list.indexOf(o1);
        int i2 = list.indexOf(o2);
        if (i1 == elementIndex) {
            // "shift" first element left
            return -1;
        } else if (i2 == elementIndex) {
            // "shift" secont element left
            return 1;
        }
        boolean left1 = i1 < elementIndex;
        boolean left2 = i2 < elementIndex;
        if (left1 != left2) {
            // different part
            return i2 - elementIndex;
        } else {
            // same part
            return i1 - i2;
        }
    }
}

答案 5 :(得分:0)

这个想法是根据列表中的原始索引比较元素的权重 - 如果当前元素的索引&gt; =指定的特定元素的索引(例如&#34; D&#34;),则索引为新列表应移到左侧,索引为&#34; D&#34;正是所需的偏移量。

int weightO1 = list.indexOf(o1) - list.indexOf(element);

在相反的情况下(list.indexOf(o1)&lt; list.indexOf(element))权重的计算应该将元素向右移动,例如它可能是

int weightO1 = list.indexOf(o1) + list.size();

我用本地方法填充重量计算:

   int weight (T o) {
    return int weight = list.indexOf(o) < list.indexOf(element) ? list.indexOf(o) + list.size() : list.indexOf(o) - list.indexOf(element);
   }

因此比较方法的实现如下:

@Override
  public int compare(T o1, T o2) {
     return weight(o1) - weight(o2); 
  }