【问题标题】:How do I sort two arrays in relation to each other?如何对彼此相关的两个数组进行排序?
【发布时间】:2012-04-30 02:33:42
【问题描述】:

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

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

所以反过来

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

我发现了这个问题:Is there an accepted Java equivalent to Python's zip(),但我宁愿(如果可能的话,并且对于胆小的人)使用我已经拥有的库(Java commons、apache commons 等)来执行此操作。如果没有其他办法,那么我会给functional java 一个机会。有什么建议吗?

【问题讨论】:

    标签: java arrays sorting


    【解决方案1】:

    如果您真的不想重做数据结构来组合信息,您可以使用 Multimap 来完成。

    此示例使用了出色的 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);
        }
    }
    

    【讨论】:

      【解决方案2】:

      完整代码如下:

      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:

      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:

      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:

      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());
          }
      
      
      }
      

      输出(先按 int 字段排序,再按 String 字段排序):

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

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

      编辑 1(附加信息):

      如果你想让它适用于任何元组,即不将字段类型限制为 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;
      
      }
      

      然后,只需相应地调整比较器,您就有了一个通用的解决方案。 编辑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);
          }
      
      }
      

      编辑 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;
          }
      
      }
      

      【讨论】:

        【解决方案3】:

        因此,显而易见的答案是将namedata 值包装在一个类中。然后维护该类的列表。该类应实现equalshashCodeComparable,然后允许使用Collections.sort 对列表进行排序。

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

        类似的东西。

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

        【讨论】:

        • 我有点明白你的意思。从未想过或尝试过,我会试一试,看看效果如何。
        【解决方案4】:

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

        例子:

        class NameAndData {
          private final String name;
          private final int data;
        }
        
        List<NameAndData> toBeSorted;
        

        然后您创建一个组合元素列表并对其进行排序。基本上,您正在编写自己的特定 Pair 类。 (我和许多 Java 开发人员认为,在 Java 中添加 Pair 类只会导致代码更加模糊——例如,LatLong 类比 Pair&lt;Double, Double&gt; 的含义要少得多。 )

        【讨论】:

        • 对不起,我真的是 Java 新手。你说的结合是什么意思?就像像字典一样将它们合并在一起并按值排序?
        • @learningJava 我更新了我的回复,让你知道我们在说什么。
        【解决方案5】:

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

        这里是一个函数,可用于根据实现Comparable (Ideone Example here) 的键对任意类型的任意数量的Lists 进行排序。


        用法

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

        // 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);
        

        输出:

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

        代码

        可以在here 找到一个 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));
        }
        

        【讨论】:

          【解决方案6】:

          您可以使用ConcurrentSkipListMap,它可以在键上提供正向和反向迭代器。如果您正在寻找除了固定的正向和反向排序之外的任意重新排序,您将不得不去别的地方。或者您可以始终保留一个简单的HashMap 或其他任何东西来维护并行项目关联,然后通过提供适当的Comparator,根据需要构造一个SortedMapTreemapConcurrentSkipListMap)。

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

          【讨论】:

          • 您可以使用 BiMap 来获得 1:1 关系:docs.guava-libraries.googlecode.com/git/javadoc/com/google/… 但在这里使用普通 Map 可能不够,因为源列表可能包含多次“键”。
          • BiMap 提供 1:1 的优点,但它本身不支持向前和向后的有序迭代。您当然可以自己手动对键进行排序,这实际上可以使您的方法在有序迭代方面更加灵活,同时保持 1:1 的关系。
          【解决方案7】:

          假设这两个数组的长度相同,您可以创建一个映射条目列表,其中包含来自这些数组的元素对,并通过键对这个列表进行倒序排序如下:

          String[] names = new String[]{"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
          int[] data = new int[]{1, 2, 3, 4, 5};
          
          List<Map.Entry<Integer, String>> entryList = IntStream
                  .range(0, names.length)
                  .mapToObj(i -> Map.entry(data[i], names[i]))
                  .sorted(Map.Entry.<Integer, String>comparingByKey().reversed())
                  .collect(Collectors.toList());
          
          System.out.println(entryList);
          // [5=Spider5, 4=Cow4, 3=Horse3, 2=Dog2, 1=Monkey1]
          

          如果要替换数组的内容:

          IntStream.range(0, entryList.size()).forEach(i -> {
              data[i] = entryList.get(i).getKey();
              names[i] = entryList.get(i).getValue();
          });
          
          System.out.println(Arrays.toString(data));
          // [5, 4, 3, 2, 1]
          System.out.println(Arrays.toString(names));
          // [Spider5, Cow4, Horse3, Dog2, Monkey1]
          

          另见:Sorting two parallel arrays

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-07-29
            • 2014-07-22
            • 1970-01-01
            • 1970-01-01
            • 2019-03-05
            • 2011-07-15
            相关资源
            最近更新 更多