【问题标题】:How to get the same output order from a Java Collection如何从 Java 集合中获得相同的输出顺序
【发布时间】:2020-05-06 20:11:30
【问题描述】:

我有一些代码:

Collection<MyGraph.MyVertex> vertCollection = graph.getVertices(); 

其中 getVertices 是 JUNG 包的一部分,其定义如下:

public interface Hypergraph<V, E> {
    Collection<E> getEdges();

    Collection<V> getVertices();
...
...
}

> If I print out the collection I may get something like [v1,v2,v4,v3]
> and on another run something like [v2,v3,v1,v4]

这让我每次都以随机顺序返回一个顶点列表。因此,我在代码其他地方的结果是不可重复的,而且很难追踪。

我希望每次都以相同的方式恢复元素的顺序。我目前的猜测是我必须将集合转换为某种保留顺序的数据结构,然后对其进行排序,以便结果是可重复的,但有没有更好的方法来做到这一点? (我不确定从集合转换为数组之类的其他东西是否会破坏其他地方的代码,因为我是集合的新手)。任何帮助都会很棒!

【问题讨论】:

    标签: java data-structures graph collections jung


    【解决方案1】:

    我了解Hypergraph.getVertices() 是您无法修改的库方法。因此,您必须操纵对该方法的调用的结果。正如Yamil's answer 所建议的,这里可以选择排序。

    但这要求您的&lt;V&gt; 实现(示例中为MyGraph.MyVertex)将实现java.lang.Comparable,或者您提供java.util.Comparator 的实现以确保您的顶点的静态排序顺序。

    java.util.Collections.sort()List 作为参数; List 的实例也始终是Collection 的实例,但不幸的是,并非Collection 的每个实例都是List 的实例——它可能是Set 甚至是绝对奇怪的东西……

    考虑到这一点,并假设 VertComparator 是我谈到的比较器,您的解决方案可能如下所示:

    …
    VertComparator comparator = new VertComparator();
    List<MyGraph.MyVertex> vertCollection = new ArrayList<>( graph.getVertices() );
    Collections.sort( vertCollection, comparator );
    …
    

    如果您程序中的所有其他代码确实只处理Collection 顶点实例(这意味着在某处没有隐藏假设它是什么类型的集合),那么您应该是安全的。

    不幸的是,即使返回的Collection 已经是List 的一个实例(… instanceof List 为真),Collections.sort() 也可能不起作用:使用List.of() 创建您的列表并尝试对其进行排序......

    通常,当一个方法返回一个Collection 以某种方式表示对象的内部状态时,这是一个不可修改的集合。显然排序也会在这里失败。所以唯一安全的方法是复制结果并对副本进行排序。

    【讨论】:

    • 所以没有办法简单地将集合转换为列表并调用一些已经内置的排序?在遍历集合的 ArrayList 版本时,我能够从顶点中获取 int ID。我必须实现自己的比较器/可比较器?
    • 不,很遗憾没有!即使集合已经是List,排序也可能不起作用。使用List.of() 创建您的列表并尝试对其进行排序......当一个方法返回一个代表对象内部状态的Collection 时,它通常是一个不可修改的集合。显然排序也会在这里失败。所以唯一安全的方法是复制结果并对副本进行排序。
    • 是的,如果您的顶点类尚未实现Comparable,您需要一个使用整数ID 进行排序的Comparator 实现。如果它不存在,你必须自己实现它;但对于 lambdas,这是一个单行代码:(v1,v2) -&gt; Integer.signum( v1.getID() - v2.getID() ),它可以直接用作调用 Collections.sort() 的第二个参数。
    • @tquadrat 当您不知道确切的值空间时,不要使用减号作为比较器。两个int 值之间的距离可能大于适合int 值空间的距离,因此可能会溢出。另一方面,这里不需要调用signum。所以正确的比较器应该是(v1,v2) -&gt; Integer.compare(v1.getID(), v2.getID())Comparator.comparingInt(v -&gt; v.getID())。或者,Comparator.comparingInt(MyGraph.MyVertex::getID)。顺便说一句,你可以使用vertCollection.sort(comparator);而不是Collections.sort( vertCollection, comparator );
    【解决方案2】:

    你可以使用这样的东西:

    List<String> names = Arrays.asList("Alex", "Charles", "Brian", "David");
    
    //Natural order
    Collections.sort(names);    //[Alex, Brian, Charles, David]
    
    //Reverse order
    Collections.sort(names, Collections.reverseOrder());    [David, Charles, Brian, Alex]   
    

    【讨论】:

      【解决方案3】:

      如您所见,JUNG 2.x 本身不提供此选项,因此您唯一真正的选择是复制输出并对其进行排序,并且假设您有一个对您有意义的Comparator顶点类型。 (这也不容易保留原始插入顺序。)

      Guava graph library,也称为common.graph,由JUNG 3.0(开发中)使用,并且在几个不同的级别上支持稳定排序:

      1. Immutable graphs' 访问器每个都返回一个具有稳定排序的Set

      2. 如果您想要mutable graph,使用GraphBuilder(及其同级类型)构造的内置实现可以使用nodeOrder() 指定图形节点应为以下任何一种:

        • 插入顺序(默认)
        • 无序
        • 自然有序(如果您的节点类型实现Comparable
        • 已排序

        对于Networks(类似于JUNG Graph 类型),您可以对图的边缘施加相同的顺序。

      3. 1234563

      (免责声明:我共同创建了 JUNG 并仍在维护它,还创建了 common.graph。)

      【讨论】:

      • 这个人自己哇!感谢图书馆!我的乱序问题特别与我在 singleSourceShortestPath() 定义中打印出源顶点有关,每次我运行 dijkstras 时它都会打印不同的节点顺序。这是 JUNG 的预期行为吗?如果是这样,是否有一种解决方法来获得确定的节点馈送/运行算法?如果这不是预期的行为,可能是因为我正在使用具有 DijkstraDistance/ShortestPath 的一些自定义实现的代码库。
      • 我只是想对 Collection 进行排序,因为我看到 getVertices() 调用让事情变得乱七八糟,所以我认为这可能与乱序算法运行有关,但在对集合进行排序后,它仍然会乱序打印源
      • 我不确定您所说的“在... singleSourceShortestPath() 中打印源顶点”是什么意思。你是说你得到了一条不同的最短路径吗?我认为这是一个不同的问题,应该单独提出,最好是使用示例代码。 :)
      • singleSourceShortestPath(source, targets, numDests) 指的是JUNG的DijkstraDistance类中“实现Dijkstra的单源加权图最短路径算法”的方法。当我运行它时,我立即打印出源代码,结果每次都不同。我不认为它有不同的路径,而是每次都在 diff/random 节点上启动算法。我正在考虑改用 DirectedOrderedSparseMultigraphs,但是我想我以后可能会发布一个新问题。
      • 因为第一个源每次都是 diff,所以下一个源也是 diff,每次都会以完全不同的遍历顺序结束。我基本上只是希望算法以可重复的方式运行,其中每次迭代的遍历/源的顺序是相同的,但我不确定是否可以使用 JUNG
      【解决方案4】:

      只需使用 ArrayList 来实现 Collection。 ArrayList 的行为就像一个数组。它们按照放入时的相同顺序出来。

      Collection<E> list = new ArrayList<>();
      Collection<V> list = new ArrayList<>();
      

      【讨论】:

      • 因为Hypergraph.getVertices() 是——根据我对描述的理解——只是由 TO 调用的库函数,这根本不是一个选项。
      猜你喜欢
      • 1970-01-01
      • 2016-06-19
      • 2016-04-23
      • 2017-03-14
      • 1970-01-01
      • 1970-01-01
      • 2015-09-17
      • 2016-08-16
      • 2021-05-12
      相关资源
      最近更新 更多