java 我可以对两个相互关联的列表进行排序吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10213493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 00:04:11  来源:igfitidea点击:

Can I sort two lists in relation to each other?

javasorting

提问by Error_404

I prototype in python and I'm used the zip function for this, I'm not sure how to do this in Java. Basically I have two lists(one is names and one is data) and want them sorted in relation to each other. My program only processes a list(data, in this case) but I use the names as a reference to what data I'm processing and I want to try to experiment with processing my data in a different order. Here's an example of the structure(in reality my data is not given to me stored but I would do either a basic sort or a reverse sort on it, nothing fancy).

我在 python 中创建原型,我为此使用了 zip 函数,我不确定如何在 Java 中执行此操作。基本上我有两个列表(一个是名称,一个是数据)并希望它们相互排序。我的程序只处理一个列表(在这种情况下是数据),但我使用名称作为我正在处理的数据的参考,我想尝试以不同的顺序处理我的数据。这是结构的一个例子(实际上我的数据并没有提供给我存储,但我会对其进行基本排序或反向排序,没什么特别的)。

String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
int[] data = new int[] {1,2,3,4,5};

so the inverse would be

所以逆将是

name = Spider5, Cow4, Horse3, Dog2, Monkey1
data = 5,4,3,2,1

I found this question: Is there an accepted Java equivalent to Python's zip()?but I would rather(if possible and for the faint of heart) do this using libraries I already have(Java commons, apache commons,etc). If there's no other way then I'll give functional javaa shot. Any suggestions?

我发现了这个问题:是否存在与 Python 的 zip() 等效的公认 Java?但我更愿意(如果可能的话,胆小的人)使用我已经拥有的库(Java commons、apache commons 等)来做这件事。如果没有其他办法,那我就functional java试一试。有什么建议?

回答by Fabian Zeindl

If you really don't want to redo your data-structures to combine the infos, you can use a Multimap to do it.

如果你真的不想重做你的数据结构来组合信息,你可以使用 Multimap 来做到这一点。

This example utilizes the excellent Google-Guava Library, which you should be using anyway :) https://code.google.com/p/guava-libraries/

此示例利用了出色的 Google-Guava 库,无论如何您都应该使用它:) https://code.google.com/p/guava-libraries/

String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
int[] data = new int[] {1,2,3,4,5};

/* guava, throws an IllegalStateException if your array aren't of the same length */
Preconditions.checkState(names.length == data.length, "data and names must be of equal length");

/* put your values in a MultiMap */
Multimap<String, Integer> multiMap = LinkedListMultimap.create();
for (int i=0; i<names.length; i++) {
    mmap.put(names[i], data[i]);
}

/* our output, 'newArrayList()' is just a guava convenience function */
List<String> sortedNames = Lists.newArrayList();
List<Integer> sortedData = Lists.newArrayList();

/* cycle through a sorted copy of the MultiMap's keys... */
for (String name : Ordering.natural().sortedCopy(mmap.keys())) {

    /* ...and add all of the associated values to the lists */
    for (Integer value : mmap.get(name)) {
        sortedNames.add(name);
        sortedData.add(value);
    }
}

回答by Greg Kramida

Here's complete code:

这是完整的代码:

StringIntTuple.java:

StringIntTuple.java:

public class StringIntTuple{
    public final int intValue;
    public final String stringValue;
    public StringIntTuple(int intValue, String stringValue){
        this.intValue = intValue;
        this.stringValue = stringValue;
    }
    public String toString(){
        return "(" + this.intValue + ", " + this.stringValue + ")";
    }

}

StringIntTupleStringComparator.java:

StringIntTupleStringComparator.java:

import java.util.Comparator;


public class StringIntTupleStringComparator implements
        Comparator<StringIntTuple> {

    @Override
    public int compare(StringIntTuple a, StringIntTuple b) {
        // TODO Auto-generated method stub
        return a.stringValue.compareTo(b.stringValue);
    }

}

StringIntTupleIntComparator.java:

StringIntTupleIntComparator.java:

import java.util.Comparator;


public class StringIntTupleIntComparator implements Comparator<StringIntTuple> {

    @Override
    public int compare(StringIntTuple a,
            StringIntTuple b) {
        return ((Integer)a.intValue).compareTo((Integer)b.intValue);
    }

}

Driver.java:

驱动程序.java:

import java.util.ArrayList;
import java.util.Collections;


public class Driver {

    /**
     * @param args
     */
    public static String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
    public static int[] data = new int[] {1,2,3,4,5};
    public static void main(String[] args) {
        ArrayList<StringIntTuple> list = new ArrayList<StringIntTuple>();
        for(int i =0; i<names.length; i++){
            list.add(new StringIntTuple(data[i],names[i]));
        }
        Collections.sort(list, new StringIntTupleIntComparator());
        System.out.println(list.toString());
        Collections.sort(list, new StringIntTupleStringComparator());
        System.out.println(list.toString());
    }


}

Output (sorted first by int field, then by String field):

输出(首先按 int 字段排序,然后按 String 字段排序):

[(1, Monkey1), (2, Dog2), (3, Horse3), (4, Cow4), (5, Spider5)]

[(1, Monkey1), (2, Dog2), (3, Horse3), (4, Cow4), (5, Spider5)]

[(4, Cow4), (2, Dog2), (3, Horse3), (1, Monkey1), (5, Spider5)]

[(4, Cow4), (2, Dog2), (3, Horse3), (1, Monkey1), (5, Spider5)]

EDIT 1 (extra info):

编辑 1(额外信息):

If you want to make this work for any Tuple, i.e. which doesn't constrain the field types to int, String, you can simply do the same operation with generics, i.e.:

如果您想对任何元组(即不将字段类型限制为 int、String)进行此操作,您可以简单地对泛型执行相同的操作,即:

public class Tuple<A,B>{
    public Tuple(A aValue, B bValue){
        this.aValue = aValue;
        this.bValue = bValue;
    }
    public final A aValue;
    public final B bValue;

}

Then, just tweak the Comparators accordingly, and you have a generic solution. EDIT 2(After lunch): Here it is.

然后,只需相应地调整比较器,您就有了一个通用的解决方案。编辑 2(午餐后):就是这样。

public class TupleAComparator<A extends Comparable<A>,B extends Comparable<B>> implements Comparator<Tuple<A,B>> {

    @Override
    public int compare(Tuple<A, B> t1, Tuple<A, B> t2) {
        return t1.aValue.compareTo(t2.aValue);
    }

}

EDIT 3: Code supplement as answer to Comment #1 (augmenting comment #2) TupleArrayList.java:

编辑 3:代码补充作为评论 #1(增加评论 #2)TupleArrayList.java 的答案:

import java.util.ArrayList;
import java.util.List;


public class TupleArrayList<A,B> extends ArrayList<Tuple<A,B>> {

    /**
     * An ArrayList for tuples that can generate a List of tuples' elements from a specific position within each tuple
     */
    private static final long serialVersionUID = -6931669375802967253L;

    public List<A> GetAValues(){
        ArrayList<A> aArr = new ArrayList<A>(this.size());
        for(Tuple<A,B> tuple : this){
            aArr.add(tuple.aValue);
        }
        return aArr;
    }

    public List<B> GetBValues(){
        ArrayList<B> bArr = new ArrayList<B>(this.size());
        for(Tuple<A,B> tuple : this){
            bArr.add(tuple.bValue);
        }
        return bArr;
    }

}

回答by Louis Wasserman

The "right" way to do this in Java is to create a combined object that holds the corresponding elements, and to sort that.

在 Java 中执行此操作的“正确”方法是创建一个包含相应元素的组合对象,然后对其进行排序。

Example:

例子:

class NameAndData {
  private final String name;
  private final int data;
}

List<NameAndData> toBeSorted;

and then you create a list of the combined elements and sort that. Basically, you're writing your own specific Pairclass. (I, and many Java developers, think that adding a Pairclass to Java would just lead to more obfuscated code -- a LatLongclass, for example, is much less ambiguous about what it means than a Pair<Double, Double>.)

然后创建一个组合元素列表并对其进行排序。基本上,您正在编写自己的特定Pair类。(我和许多 Java 开发人员都认为,向PairJava添加一个类只会导致代码更加混乱——LatLong例如,一个类的含义比Pair<Double, Double>.更明确。)

回答by John B

So the obvious answer here is to wrap the nameand datavalues in a class. Then maintain a List of that class. The class should implement equals, hashCodeand Comparablewhich then then allow sorting the list using Collections.sort.

所以这里显而易见的答案是将namedata值包装在一个类中。然后维护一个该类的列表。类应该实现equalshashCode并且Comparable然后再允许使用排序列表Collections.sort

Maintain related data in two different lists is anti-OOP.

在两个不同的列表中维护相关数据是反 OOP 的。

Something like this.

像这样的东西。

class MyWrapper implements Comparable<MyWrapper>{
   private String name;
   private int data;
}

List<MyWrapper> listToBeSorted;

回答by Kevin Welker

You could use a ConcurrentSkipListMapwhich can provide forward and reverse iterators over the keys. If you are looking for arbitrary re-orderings besides a fixed forward and reverse ordering, you'll have to go to something else. Or you can always keep a simple HashMapor whatever to maintain parallel item associations, and then construct a SortedMap(Treemapor ConcurrentSkipListMap) as needed by providing an appropriate Comparator.

您可以使用ConcurrentSkipListMap可以在键上提供正向和反向迭代器的 a 。如果您正在寻找除固定的正向和反向排序之外的任意重新排序,则您将不得不进行其他操作。或者,您可以始终保持一个简单的HashMap或任何保持平行项关联,然后构造一个SortedMapTreemapConcurrentSkipListMap根据需要通过提供适当的)Comparator

The disadvantage of this approach is that the associations between keys/values are much more transient, and can be more easily and accidentally broken by updates to the map. All of the other answers that create Tuples, Pairs, or other explicit 1-1 relationships address that better. Of course, if you intend for the associations to be more fluid, then just using a map adds a bit of an advantage.

这种方法的缺点是键/值之间的关联更加短暂,并且更容易被更新映射而意外破坏。创建元组、对或其他明确的 1-1 关系的所有其他答案都更好地解决了这个问题。当然,如果您希望关联更加流畅,那么仅使用地图会增加一些优势。

回答by bcorso

In some cases it doesn't make much sense to create a new class just to do concurrent sorting.

在某些情况下,创建一个新类只是为了进行并发排序并没有多大意义。

Here, is a function that can be used to sort an arbitrary number of Lists with arbitrary types based on a key that implement Comparable(Ideone Example here).

这是一个函数,可用于List根据实现的键Comparable此处为 Ideone 示例)对任意数量的任意类型的s进行排序。



Usage

用法

Here is an example of how you can use the function to sort multiple lists of arbitrary types:

下面是一个示例,说明如何使用该函数对多个任意类型的列表进行排序:

// Can be any type that implements Comparable, Dupes are allowed
List<Integer> key = Arrays.asList(4, 3, 1, 2, 1);

// List Types do not need to be the same
List<String> list1 = Arrays.asList("Four", "Three", "One", "Two", "One");
List<Character> list2 = Arrays.asList('d', 'c', 'a', 'b', 'a');

// Sorts key, list1, list2
// Remove second key if you don't want to sort key.
multiSort(key, key, list1, list2);

Output:

输出:

key:   [1, 1, 2, 3, 4]
list1: [One, One, Two, Three, Four]
list2: [a, a, b, c, d]


Code

代码

An Ideone Example can be found herewhich includes validation of parameters and a test case.

可以在此处找到 Ideone 示例其中包括参数验证和测试用例。

public static <T extends Comparable<T>> void multiSort(
    final List<T> key, List<?>... lists){
  // Create a List of indices
  List<Integer> indices = new ArrayList<Integer>();
  for(int i = 0; i < key.size(); i++) {
    indices.add(i);
  }

  // Sort the indices list based on the key
  Collections.sort(indices, new Comparator<Integer>() {
    @Override public int compare(Integer i, Integer j) {
      return key.get(i).compareTo(key.get(j));
    }
  });

  // Create a mapping that allows sorting of the List by N swaps.
  // Only swaps can be used since we do not know the type of the lists
  Map<Integer,Integer> swapMap = new HashMap<Integer, Integer>(indices.size());
  List<Integer> swapFrom = new ArrayList<Integer>(indices.size()),
                swapTo   = new ArrayList<Integer>(indices.size());
  for (int i = 0; i < key.size(); i++) {
    int k = indices.get(i);
    while (i != k && swapMap.containsKey(k)) {
      k = swapMap.get(k);
    }

      swapFrom.add(i);
      swapTo.add(k);
      swapMap.put(i, k);
  }

  // use the swap order to sort each list by swapping elements
  for (List<?> list : lists)
    for (int i = 0; i < list.size(); i++)
      Collections.swap(list, swapFrom.get(i), swapTo.get(i));
}