借助索引排序列表

时间:2017-05-03 09:59:38

标签: java java-stream

我能以某种方式访问​​列表中对象的索引吗?

myList.stream().sorted((o1, o2) -> 0).collect(Collectors.toList())

e.g:

我希望首先显示奇数索引,最后显示索引。

7 个答案:

答案 0 :(得分:5)

我不认为基于索引的重新排序操作是实际的排序操作。例如,没有人会考虑实施像Collections.reverse(List)这样的操作作为排序操作。

将奇数位置的元素移动到前面的有效方法是

public static <T> void oddFirst(List<T> list) {
    final int size = list.size();
    for(int index1=0, index2=list.size()/2|1; index2<size; index1+=2, index2+=2)
        Collections.swap(list, index1, index2);
}

或者,您可以流式传输this answer中的索引,以生成新的List

答案 1 :(得分:2)

过滤器可能有所帮助:

List<Integer> listEvenIndicesMembers = IntStream.range(0, list.size()).filter(n -> n % 2 == 0).mapToObj(list::get)
            .collect(Collectors.toList());
List<Integer> listOddIndicesMembers = IntStream.range(0, list.size()).filter(n -> n % 2 != 0).mapToObj(list::get)
            .collect(Collectors.toList());
System.out.println(listEvenIndicesMembers);
System.out.println(listOddIndicesMembers);
  

[1,3,5,7,9,11]

     

[2,4,6,8,10]

问题是现在你有2个列表,一个接一个地追加将产生你想要的相同...我仍在检查文档,也许我找到更优雅/优化的东西。

编辑:

感谢@Holger提出的建议:

你可以连接流:

List<Integer> listOptimal = IntStream
            .concat(IntStream.range(0, list.size()).filter(n -> n % 2 == 0),
                    IntStream.range(0, list.size()).filter(n -> n % 2 != 0))
            .mapToObj(list::get).collect(Collectors.toList());
System.out.println(listOptimal);

答案 2 :(得分:1)

我认为ΦXocę 웃 Пepeúpa ツ的解决方案对你来说应该没问题,这里只是另一种选择(记住,它只是一种替代方法,为了学习,这个解决方案不应该用于'现实生活',它只是为了显示可能性):

public static void main(String arg[]) {
  List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H");
  List<String> listCopy = new ArrayList<>(list);

  list.sort((o1, o2) -> Integer.compare(listCopy.indexOf(o2) % 2, listCopy.indexOf(o1) % 2));

  System.out.println(list);
}

输出为:[B, D, F, H, A, C, E, G]

答案 3 :(得分:1)

接受的答案有效,但这也有效(只要列表中没有重复项):

// a list to test with
List<String> list = Arrays.asList("abcdefghijklmnopqrstuvwxyz".split(""));

List<String> list2 = list.stream()
   .sorted(Comparator.comparing(x -> list.indexOf(x) % 2).thenComparing(x -> list.indexOf(x)))
   .collect(Collectors.toList());
list2.forEach(System.out::print);

首先打印奇数索引,然后打印偶数索引

acegikmoqsuwybdfhjlnprtvxz

只是为了说明霍尔格在评论中提出的观点。

  • 这个答案的解决方案让我的机器运行了75毫秒。
  • this answer中的解决方案仅用了3毫秒。
  • 而霍尔格自己的answer最终以令人惊讶的方式结束了。 0毫秒。

答案 4 :(得分:1)

您可以使用装饰器模式存储对象以及用于排序的额外信息。例如。如果你有一个你想要排序的字符串列表(添加getter):

class StringWithIndex {
    String text;
    int index;
    int evenIndex;

    StringWithIndex(String text, int index) {
        this.text = text;
        this.index = index;
        this.evenIndex = index % 2 == 0 ? 1 : -1;
    }
}

然后你可以对这些对象进行排序而不是字符串:

List<String> strings = Arrays.asList("a", "b", "c", "d");

List<String> sorted = IntStream.range(0, strings.size())
        .mapToObj(i -> new StringWithIndex(strings.get(i), i))
        .sorted(comparingInt(StringWithIndex::getEvenIndex).thenComparingInt(StringWithIndex::getIndex))
        .map(StringWithIndex::getText)
        .collect(Collectors.toList());

这会增加一些开销来创建临时对象并需要另一个类。但随着排序规则变得更加复杂,它可以证明非常有用。

答案 5 :(得分:0)

如果您只想根据索引移动List值,那么请参阅我的其他答案。

但是,您问题的原始措辞表明您希望将sort()sorted()与比较器一起使用,该比较器会考虑现有位置以及该值的其他方面。

如果你刚刚使用Collections.sort(),这也很困难,因为那里使用的Comparator也无法访问索引。

您可以将Stream<T>映射到Stream<Entry<Integer,T>>,并在完成后将其映射回Stream<T>

(这是AbstractMap.SimpleEntry - 因为它存在于标准库中 - 您也可以编写自己的Pair或使用第三方中的一个 - 请参阅A Java collection of value pairs? (tuples?))< / p>

mystream.map(addIndex()).sorted(compareEntry()).map(stripIndex()) ...

private <T> Function<T,Entry<Integer,T>> addIndex() {
    final int[] index = new int[] { 0 };
    return o -> new SimpleEntry(index[0]++, o);    
}

private Comparator<Entry<Integer, T>> compareEntry() {
    return a,b -> {
       // your comparison code here -- you have access to
       // getKey() and getValue() for both parameters
    };
}

private <T> Function<Entry<Integer,T>, T> stripIndex() {
    return e -> e.getValue();
}

请注意,addIndex()至少是不可并行的。我想,一旦所有条目都用索引标记,那么下游就可以并行完成。

答案 6 :(得分:0)

奖励回答 - 如果您要做的就是创建一个包含奇数条目后跟偶数条目的新列表,那么使用Stream会增加不必要的复杂性。

 for(int i=0; i<inlist.size(); i+=2) {
     outlist.add(inlist.get(i));
 }
 for(int i=1; i<inlist.size(); i+=2) {
     outlist.add(inlist.get(i));
 }

还有一个非常简单的算法,可以在适当的位置重新排序列表 - 您可以自己编写。想一想 - 只要知道列表长度,就可以从旧索引中获取新索引。