【问题标题】:How to Order one list by the index values stored in another list如何通过存储在另一个列表中的索引值对一个列表进行排序
【发布时间】:2018-01-04 18:49:29
【问题描述】:

我有一个BigDecimal List 如:

[50.0,26.2,29.3]

然后,我有另一个ListIntegers

[2,1,0]

这些整数指的是我想要重新排序第一个列表的索引。所以,我想将第一个列表重新排序为:

[29.3,26.2,50.0]

我正在使用 Java 8。

【问题讨论】:

  • 你的尝试在哪里?
  • 该示例没有明确目标是什么,特别是索引是针对源列表还是针对目标列表。如果索引用于源列表,则重新排序的副本将是 dst[i] = src[index[i]],或者如果索引用于目标列表(有时称为“排名”),则重新排序的副本将是dst[索引[i]] = src[i]。一个更清晰的示例将重新排序所有 3 个(或更多)元素,而不是将中间元素留在原处。根据接受的答案,索引似乎适用于源列表。
  • 对于基于源的索引,可以使用 O(n) 时间复杂度就地执行此操作,如 prior thread 所示,但索引列表也得到“排序”。
  • @rcgldr +1 - 好点。我已经更新了我的帖子。
  • @James - 感谢您的解释。所以这里的顺序是将原始数组与索引数组一起排序,以生成用于恢复原始数组的秩数组。另一种方法是不修改原始数组,仅根据最大余数对索引进行排序,但这可能会由于随机访问大小数而变慢(在我的系统上,在对元素进行排序时会发生权衡,在这个如果使用归并排序,大小写为大十进制,大小介于 128 和 256 字节之间。

标签: java list sorting


【解决方案1】:

原解决方案

BigDecimal 的第一个列表称为decListInteger 的第二个列表称为intList

intList.stream().map(decList::get).collect(Collectors.toList());

这将获取第二个列表的每个值并将其用作访问第一个列表的值的索引。然后将它们收集到一个新的List<BigDecimal>

(编辑)使用LinkedLists 检查效率

这是值得深思的,上述解决方案通常就足够了

TLDR

LinkedLists 唯一会损害我原来的解决方案的地方是“值列表”(在这种情况下是 ListBigDecimals)是 LinkedList

本次测试的原因

由于ArrayLists 上的getO(1),但LinkedLists 上的getO(N),可能会有其他更快的解决方案。

我想检查使用Iterator 的解决方案对于LinkedLists 是否会更快。我通读了各种 Javadocs,找不到运行 linkedList.stream().map(..) 是否会使用 .iterator 代替 LinkedLists 而不是调用 get。因此,我决定安排一次实际测试的时间。

测试用例

  1. 使用ArrayLists 使用流和映射测试上述原始解决方案。
  2. 使用LinkedLists 使用流和映射测试上述原始解决方案。
  3. 使用显式 .iteratorLinkedLists 测试解决方案。
  4. 使用流和映射测试上述原始解决方案,使用ArrayList 作为索引,使用LinkedList 作为值。
  5. 使用流和映射测试上面的原始解决方案,使用LinkedList 作为索引,使用LinkedList 作为值。

测试结果

ArrayList Implementation:
Duration: 70 milliseconds
Duration: 15 milliseconds
Duration: 16 milliseconds
Duration: 15 milliseconds
Duration: 15 milliseconds
Average duration: 26 milliseconds

LinkedList Implementation with Stream and Map:
Duration: 1359 milliseconds
Duration: 1387 milliseconds
Duration: 1309 milliseconds
Duration: 1302 milliseconds
Duration: 1304 milliseconds
Average duration: 1332 milliseconds

LinkedList Implementation with Iterator:
Duration: 2806 milliseconds
Duration: 2173 milliseconds
Duration: 1305 milliseconds
Duration: 1305 milliseconds
Duration: 1305 milliseconds
Average duration: 1778 milliseconds

Mix test case #4:
Duration: 1281 milliseconds
Duration: 1278 milliseconds
Duration: 1278 milliseconds
Duration: 1278 milliseconds
Duration: 1278 milliseconds
Average duration: 1278 milliseconds

Mix test case #5:
Duration: 13 milliseconds
Duration: 7 milliseconds
Duration: 7 milliseconds
Duration: 7 milliseconds
Duration: 7 milliseconds
Average duration: 8 milliseconds

结论

  • ArrayLists 的原始解决方案比 LinkedLists 快得多,因为 O(N)O(N^2)
  • 似乎streams 已经使用iterators,或类似的增强功能来解决get 效率差异。从测试用例 2 和 3 之间的相似性可以看出这一点。
  • LinkedLists 仅在它们包含此算法中的值时才会影响效率,这是由于流的迭代器优化。请注意测试用例 #5 与仅使用 ArrayLists 一样快,尽管它如何使用 LinkedList 作为索引。

效率测试来源

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.math.BigDecimal;
import java.util.stream.Collectors;
import java.lang.Math;
import java.util.Iterator;

class Test {
  public static void main(String[] args) {
    testArrayListImplementation();
    testLinkedListImplementation();
    testCaseFourMixed();
    testCaseFiveMixed();
  }

  static void testArrayListImplementation() {
    List<BigDecimal> bigDecList = new ArrayList<BigDecimal>();
    List<Integer> ndxList = new ArrayList<Integer>();

    System.out.println("ArrayList Implementation:");
    timeListImplementation(bigDecList, ndxList, false);
  }

  static void testLinkedListImplementation() {
    List<BigDecimal> bigDecList = new LinkedList<BigDecimal>();
    List<Integer> ndxList = new LinkedList<Integer>();

    System.out.println("LinkedList Implementation with Stream and Map:");
    timeListImplementation(bigDecList, ndxList, false);

    System.out.println("LinkedList Implementation with Iterator:");
    timeListImplementation(bigDecList, ndxList, true);
  }

  static void testCaseFourMixed() {
    //Test case 4
    List<BigDecimal> bigDecList = new LinkedList<BigDecimal>();
    List<Integer> ndxList = new ArrayList<Integer>();

    System.out.println("Mix test case #4:");
    timeListImplementation(bigDecList, ndxList, false);
  }

  static void testCaseFiveMixed() {
    //Test case 5
    List<BigDecimal> bigDecList = new ArrayList<BigDecimal>();
    List<Integer> ndxList = new LinkedList<Integer>();

    System.out.println("Mix test case #5:");
    timeListImplementation(bigDecList, ndxList, false);
  }

  static void timeListImplementation(List<BigDecimal> bigDecList, List<Integer> ndxList, boolean useIterator) {
    for (int i = 0; i < 10000; i++) {
      bigDecList.add(new BigDecimal(123.4));
      ndxList.add((int) (Math.random() * 1000));
    }

    long totalDuration = 0;

    for (int linkedTrial = 0; linkedTrial < 5; linkedTrial++) {
      long startTime = System.nanoTime();

      for (int numComputations = 0; numComputations < 100; numComputations++) {
        if (useIterator) {
          Iterator<Integer> ndxIter = ndxList.iterator();
          List<BigDecimal> specializedList = new LinkedList<BigDecimal>();
          while (ndxIter.hasNext()) {
            specializedList.add(bigDecList.get(ndxIter.next()));
          }
        } else {
          List<BigDecimal> specializedList = ndxList.stream().map(bigDecList::get).collect(Collectors.toList());
        }
      }

      long endTime = System.nanoTime();
      long duration = (endTime - startTime) / 1000000; //milliseconds

      System.out.println("Duration: " + duration + " milliseconds");
      totalDuration += duration;
    }
    System.out.println("Average duration: " + (totalDuration / 5) + " milliseconds");
  }
}

【讨论】:

  • 实际上是 O(N)... 如果是 ArrayList
  • @FedericoPeraltaSchaffner 如果只有由常识驱动的软件... :)
  • @Eugene 如果列表是 ArrayList,那么是的,因为 ArrayList.get 是 O(1) 并且您正在执行 N 次获取。如果它是 LinkedList,则不,因为 LinkedList.get 是 O(N),并且执行 N 次获取将产生 O(N^2)。一般来说,排序算法是 O(NlogN),虽然我不确定在 JDK 中使用的 TimSort。但是,您评论中的代码发出了一个 list.indexOf 调用,它是 O(N),inside 比较器。如果比较器内的代码是 O(1),那么排序算法是 O(NlogN),但是使用 list.indexOf 调用,我相信它需要 O(N^2logN) 来对整个流进行排序。
  • @Eugene 或者如果 list.indexOf 被调用 log(N) 次而不是 N 次,它可能仍然是 O(NlogN),这取决于 TimSort 实现。无论如何,这个答案的代码具有更好的性能。
  • @MattGoodrich 恭喜编辑完成。你真的花了一些时间来制作它。您的测试显示了我们已经知道的:LinkedList.get 是 O(N),因此使用它的解决方案很糟糕。您还展示了流不使用 get(i) 来提供元素,尽管实现是基于 Spliterator 而不是 Iterator。尽管您的测试显示了有效的努力,但也许您应该阅读 JMH 以及如何进行正确的微基准测试(在 SO 上搜索问题)。它将启发您,您一定会了解可能使您的测试无效的因素。
【解决方案2】:

嗯,首先是访问 BigDecimal 中的值。 使用父类给定的 BigDecimal 中的属性。

**Loop the values**:
  BigDecimal.add(BigDecimal.get(List.get(int index)));

**Delete the amount of times that the loop ran:** 
  BigDecimal.remove(int index);

BigDecimal 的对象应根据索引重新排序为正确的值。但是,该函数以 2N 运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-07
    • 2020-10-25
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 2021-07-14
    • 2021-01-23
    • 1970-01-01
    相关资源
    最近更新 更多