【问题标题】:Java - Sort one array based on values of another array?Java - 根据另一个数组的值对一个数组进行排序?
【发布时间】:2015-04-17 19:57:46
【问题描述】:

我有一个字符串数组,它们是来自外部代码的类的实例,我不想更改。

我还有一个整数数组,它是通过在每个对象上调用一个函数生成的。所以我有

答:[string1, string2, string3]

乙:[40, 32, 34]

我如何轻松地对 A 进行排序,使其按 B 的值排序。我有可用的 boost。我想对 A 进行排序,使其按顺序排列:

[string2, string3, string1]

在 javascript 中你可以这样做:

B.sort(function(a,b){return A[B.indexOf(a)] < A[B.indexOf(b)];});

【问题讨论】:

  • 创建一个自定义比较器,它首先将A 中的值与B 中的值(在构造函数中)关联并使用它进行排序
  • 你能不能只实现与你的 JavaScript 示例相同的东西作为 Comparator
  • 您需要编写自定义比较器。
  • 你也可以使用 TreeMap(B 中的值作为键,A 中的对应值作为值),因为 B 中的值是唯一的。

标签: java algorithm sorting


【解决方案1】:

正如@wassgren 所说,您可以使用流,但不必创建类,只需使用索引即可:

String[] strings = {"string1", "string2", "string3"};
int[] boosts = {40, 32, 34};

String[] sorted = IntStream.range(0, boosts.length).boxed()
        .sorted(Comparator.comparingInt(i -> boosts[i]))
        .map(i -> strings[i])
        .toArray(String[]::new);

首先创建一个索引流,然后根据提升对它们进行排序,然后在该索引中获取字符串。

【讨论】:

    【解决方案2】:

    在 java 8 中,您可以这样做

    使用 lambda:

        String[] strings = new String[]{"string1", "string2", "string3"};
        final int[] ints = new int[]{40, 32, 34};
    
        final List<String> stringListCopy = Arrays.asList(strings);
        ArrayList<String> sortedList = new ArrayList(stringListCopy);
        Collections.sort(sortedList, (left, right) -> ints[stringListCopy.indexOf(left)] - ints[stringListCopy.indexOf(right)]);
    

    或者更好,使用 Comparator

        String[] strings = new String[]{"string1", "string2", "string3"};
        final int[] ints = new int[]{40, 32, 34};
    
        final List<String> stringListCopy = Arrays.asList(strings);
        ArrayList<String> sortedList = new ArrayList(stringListCopy);
        Collections.sort(sortedList, Comparator.comparing(s -> ints[stringListCopy.indexOf(s)]));
    

    【讨论】:

    • 这个逻辑不适用于不同的例子,测试第一个用例:请测试:String[] strings = new String[]{"string1", "string2", "string3" , "字符串4"};最终 int[] ints = new int[]{100,88, 98, 92};得到的结果: Array after sort[string2, string1, string4, string3] (但string1应该在最后,你能解释或修复吗?
    • 您需要复制到另一个数组,并在未更改的数组上进行排序,否则在其他示例中将不起作用。
    • 该算法在性能方面效率不高,因为每个 indexOf(s) 都以线性时间运行以查找索引。所以最坏情况的性能是 O(nnlogn),而不是 O(n*logn)
    【解决方案3】:

    在java中你需要有两个数组一个副本来排序和你想要排序的数组。

    使用 lambda:

    String[] strings = new String[]{"string1", "string2", "string3", "string4"};
    final int[] ints = new int[]{100, 88, 92, 98};
    
    final List<String> stringListCopy = Arrays.asList(strings);
    ArrayList<String> sortedList = new ArrayList(stringListCopy);
    Collections.sort(sortedList, (left, right) -> ints[stringListCopy.indexOf(left)] - ints[stringListCopy.indexOf(right)]);
    

    或使用比较器:

    String[] strings = new String[]{"string1", "string2", "string3", "string4"};
    final int[] ints = new int[]{100, 92, 88, 98};
    
    final List<String> stringListCopy = Arrays.asList(strings);
    ArrayList<String> sortedList = new ArrayList(stringListCopy);
    Collections.sort(sortedList, Comparator.comparing(s -> ints[stringListCopy.indexOf(s)]));
    

    【讨论】:

      【解决方案4】:

      也许不完全适合这种情况,但对于那些寻找答案的人来说,如何根据另一个字符串对一个数组进行排序:

      // Array of values, in a order of sorting
      static final Map<String, Integer> ROUNDS_SORT = new HashMap<String, Integer>();
      static {
          ROUNDS_SORT.put("f", 0);
          ROUNDS_SORT.put("s", 1);
          ROUNDS_SORT.put("q", 2);
          ROUNDS_SORT.put("r16", 3);
          ROUNDS_SORT.put("r32", 4);
          ROUNDS_SORT.put("r64", 5);
      }
      
      // Your array to be sorted
      static ArrayList<String> rounds = new ArrayList<String>() {{
          add("f");
          add("q");
          add("q");
          add("r16");
          add("f");
      }};
      
      // implement
      public List<String> getRoundsSorted() {
          Collections.sort(rounds, new Comparator<String>() {
              @Override
              public int compare(String p1, String p2) {
                  return Integer.valueOf(ROUNDS_SORT.get(p1)).compareTo(Integer.valueOf(ROUNDS_SORT.get(p2)));
              }
          });
          return rounds;
      }
      

      【讨论】:

        【解决方案5】:

        我有一个类似的问题,并通过编写一个排序算法来解决它,该算法对一组度量进行排序,并在对象数组中进行相同的交换。这是代码,带有测试、最良好的祝愿,但没有任何承诺:

        package other;
        
        import java.util.Arrays;
        import java.util.Random;
        
        /**
         * Sorts an array of objects (<code>bags</code>) by a separate array of doubles (<code>measures</code>). 
         * It sorts into ascending order. 
         * <p>
         * The <code>results</code> array is always a new array. 
         * <p>
         * The algorithm used:<ul>
         * <li> Is (I believe) a merge-sort, which would mean it is stable. (I haven't tested this.)
         * <li> Efficiently exploits already ordered subsequences. 
         * <li> Requires the allocation of eight arrays: four of the baggage type, four of doubles, each the length of the original data. 
         * </ul>
         * <p>
         * A <code>NaN</code> in the <code>measures</code> - I haven't thought about that, and don't want to. 
         * <p>
         * There is test code at the end of the class. 
         */
        public class SortBaggageByDouble { 
        
            public final Object [] results ; 
        
            protected final int length ; 
        
            public SortBaggageByDouble(Object[] bags, double[] measures) { 
                this.length = bags.length; 
                if (bags.length!=measures.length) throw new IllegalArgumentException("Mismatched lengths: payload array "+bags.length+", measures array "+measures.length); 
                this.results = new Object[length]; 
                Object [] bagsA = new Object[length] ; 
                Object [] bagsB = new Object[length] ; 
                Object [] bagsC = new Object[length] ; 
                Object [] bagsD = new Object[length] ; 
                double [] measuresA = new double[length] ; 
                double [] measuresB = new double[length] ; 
                double [] measuresC = new double[length] ; 
                double [] measuresD = new double[length] ; 
                System.arraycopy(bags, 0, bagsA, 0, length); 
                System.arraycopy(measures, 0, measuresA, 0, length); 
                munge(length, 0, bagsA, bagsB, bagsC, bagsD, measuresA, measuresB, measuresC, measuresD); 
            }
        
            private void munge(int inLengthA, int inLengthB, Object[] inBagsA, Object[] inBagsB, Object[] outBagsC, Object[] outBagsD, double[] inMeasuresA, double[] inMeasuresB, double[] outMeasuresC, double[] outMeasuresD) { 
                int outLengthC = 0 ; 
                int outLengthD = 0 ; 
                int cursorA = 0 ; 
                int cursorB = 0 ; 
                boolean toC = true ; 
                while(outLengthC+outLengthD<length) { 
                    boolean fromA ; 
                    if (cursorA>=inLengthA) { 
                        fromA = false ; 
                    } else if (cursorB>=inLengthB) { 
                        fromA = true ; 
                    } else { 
                        fromA = inMeasuresA[cursorA] <= inMeasuresB[cursorB] ;  
                    } 
                    double tmpMeasure = fromA ? inMeasuresA[cursorA] : inMeasuresB[cursorB] ; 
                    Object tmpBag = fromA ? inBagsA[cursorA] : inBagsB[cursorB] ; 
                    if (fromA) cursorA ++ ; else cursorB ++ ; 
                    if (toC) { 
                        if (outLengthC==0 || (outMeasuresC[outLengthC-1]<=tmpMeasure)) { 
                            outMeasuresC[outLengthC] = tmpMeasure ; 
                            outBagsC[outLengthC] = tmpBag ; 
                            outLengthC ++ ; 
                        } else { 
                            toC = false ; 
                            outMeasuresD[outLengthD] = tmpMeasure ; 
                            outBagsD[outLengthD] = tmpBag ; 
                            outLengthD ++ ; 
                        }
                    } else { 
                        if (outLengthD==0 || (outMeasuresD[outLengthD-1]<=tmpMeasure)) { 
                            outMeasuresD[outLengthD] = tmpMeasure ; 
                            outBagsD[outLengthD] = tmpBag ; 
                            outLengthD ++ ; 
                        } else { 
                            toC = true ; 
                            outMeasuresC[outLengthC] = tmpMeasure ; 
                            outBagsC[outLengthC] = tmpBag ; 
                            outLengthC ++ ; 
                        }
                    }
                }
                if (outLengthC==length) { 
                    System.arraycopy(outBagsC, 0, results, 0, length); 
                } else { 
                    munge(outLengthC, outLengthD, outBagsC, outBagsD, inBagsA, inBagsB, outMeasuresC, outMeasuresD, inMeasuresA, inMeasuresB); 
                }
            }
        
            /**
             * Subclass to sort strings, with a result object <code>sortedStrings</code> which is of a useful type. 
             */
            public static class Strings extends SortBaggageByDouble { 
        
                public final String [] sortedStrings ; 
        
                public Strings(String[] in, double[] measures) {
                    super(in, measures);
                    this.sortedStrings = new String[results.length]; 
                    for (int i=0 ; i<results.length ; i++) sortedStrings[i] = (String) results[i] ; 
                } 
        
            }
        
            /**
             * Tests sorting - assumes there are no duplicates among the measures. 
             */
            private static class NoDuplicatesTest { 
                private NoDuplicatesTest(String[] shuffledStrings, double[] shuffledMeasures, String[] expectedStrings) { 
                    SortBaggageByDouble.Strings sorter = new SortBaggageByDouble.Strings(shuffledStrings, shuffledMeasures); 
                    if (!Arrays.equals(expectedStrings, sorter.sortedStrings)) throw new RuntimeException("Test failed"); 
                }
            }
        
            private static class MultiseedNoDuplicatesTest { 
                private MultiseedNoDuplicatesTest(String[] orderedStrings, double[] orderedMeasures, int[] seeds) { 
                    int length = orderedStrings.length;
                    for (int seed : seeds) { 
                        Random random = new Random(seed); 
                        int [] shuffleIndices = new int[length] ; 
                        for (int i=0 ; i<length ; i++) shuffleIndices[i] = i ; 
                        for (int i=1 ; i<length ; i++) { 
                            int j = random.nextInt(i+1); // 'j' is in the range 0..i, bounds inclusive. 
                            int tmp = shuffleIndices[i]; 
                            shuffleIndices[i] = shuffleIndices[j] ; 
                            shuffleIndices[j] = tmp ; 
                        }
                        String[] shuffledStrings = new String[length]; 
                        double[] shuffledMeasures = new double[length]; 
                        for (int i=0 ; i<length ; i++) { 
                            shuffledStrings[shuffleIndices[i]] = orderedStrings[i] ; 
                            shuffledMeasures[shuffleIndices[i]] = orderedMeasures[i] ; 
                        }
                        if (false && 0<length && length<8) { 
                            System.out.println("shuffleIndices is "+ stringfor(shuffleIndices)); 
                            System.out.println("shuffledStrings is "+ stringfor(shuffledStrings)); 
                            System.out.println("shuffledMeasures is "+ stringfor(shuffledMeasures)); 
                        }
                        new NoDuplicatesTest(shuffledStrings, shuffledMeasures, orderedStrings); 
                    }
                }
            }
        
            private static class MultilengthMultiseedNoDuplicatesTest { 
                MultilengthMultiseedNoDuplicatesTest(int[] lengths, int[] seeds) { 
                    for (int i=0 ; i<lengths.length ; i++) { 
                        int length = lengths[i] ; 
                        String[] orderedStrings = new String[length] ; 
                        double[] orderedMeasures = new double[length] ; 
                        for (int j=0 ; j<length ; j++) { 
                            orderedStrings[j] = "_"+j+"_" ; 
                            orderedMeasures[j] = j ; 
                        }
                        if (false && 0<length && length<8) { 
                            System.out.println("orderedStrings is "+ stringfor(orderedStrings)); 
                            System.out.println("orderedMeasures is "+ stringfor(orderedMeasures)); 
                        }
                        new MultiseedNoDuplicatesTest(orderedStrings, orderedMeasures, seeds); 
                    }
        
                }
            }
        
            public static class ClassTest { 
                ClassTest() { 
                    new MultilengthMultiseedNoDuplicatesTest(new int[]{0}, new int[]{8543, 45125}); 
                    new MultilengthMultiseedNoDuplicatesTest(new int[]{1}, new int[]{8543, 45125}); 
                    new MultilengthMultiseedNoDuplicatesTest(new int[]{2}, new int[]{8543, 45125, 4545, 785413}); 
                    new MultilengthMultiseedNoDuplicatesTest(new int[]{3, 4, 5, 6, 7, 8, 9, 10}, new int[]{8543, 45125, 4545, 785413}); 
                    new MultilengthMultiseedNoDuplicatesTest(new int[]{50, 100, 1000}, new int[]{474854, 43233}); 
                    //////  Passed! Bye bye.  
                    System.out.println("Passed test suite "+this.getClass().getCanonicalName()); 
                }
            }
        
            public static String stringfor(int[] array) {
                StringBuilder sb = new StringBuilder(); 
                build(sb, array);
                return sb.toString();
            }
        
            public static void build(StringBuilder sb, int[] array) { 
                for (int i=0 ; i<array.length ; i++) { 
                    if (sb.length()>0) sb.append(' '); 
                    sb.append(array[i]); 
                } 
            }
        
            public static String stringfor(double[] array) {
                StringBuilder sb = new StringBuilder(); 
                build(sb, array);
                return sb.toString();
            }
        
            public static void build(StringBuilder sb, double[] array) { 
                for (int i=0 ; i<array.length ; i++) { 
                    if (sb.length()>0) sb.append(' '); 
                    sb.append(array[i]); 
                } 
            }
        
            public static String stringfor(String[] labels) {
                StringBuffer sb = new StringBuffer();
                String sep = "" ; 
                for (int i=0 ; i<labels.length ; i++) { 
                    sb.append(sep); 
                    String label = labels[i] ; 
                    sb.append(label!=null ? label : "null"); 
                    sep = ", " ; 
                }
                return sb.toString();
            }
        
        }
        

        【讨论】:

          【解决方案6】:

          我通过使用 Comparator 接口解决了这个问题。

           import java.util.Comparator;
           import java.util.Collections;
           import java.util.List;
           import java.util.Arrays;
          
          
           public class ComparatorDemo {
          
           public static void main(String[] args) {
              List<Area> metaData = Arrays.asList(
                      new Area("Joe", 24),
                      new Area("Pete", 18),
                      new Area("Chris", 21),
                      new Area("Rose",21)
              );
          
              Collections.sort(metaData, new ResultComparator());
              for(int i =0 ;metaData.size()>i;i++)
                       System.out.println(metaData.get(i).output);
          
          
            }
           }
          
          
           class ResultComparator implements Comparator<Area> {
               @Override
               public int compare(Area a, Area b) {
                   return a.result < b.result ? -1 : a.result == b.result ? 0 : 1;
               }
           }
          
           class Area{
             String output;
             int result;
          
          Area(String n, int a) {
              output = n;
              result = a;
               }
           }
          

          【讨论】:

          • 错误:MainActivity.ResultCompare 不是抽象的,并且不会覆盖 Comparator 中的抽象方法 compare(Long,Long)。类 ResultCompare 实现 Comparator{
          【解决方案7】:

          简短回答:我建议创建一个单独的类来保存有关实际 String 和提升(int)的信息。如果您假设以下情况:

          public class BoostString {
              int boost;
              String str;
          
              public BoostString(int boost, String str) {
                  this.boost = boost;
                  this.str = str;
              }
          }
          

          然后,您可以使用 Comparator 对数组进行排序,它与 Java 8 Streaming API 配合得特别好。

          String[] strings = {"string1", "string2", "string3"};
          int[] boosts = {40, 32, 34};
          
          final String[] sorted = IntStream.range(0, boosts.length)
                  .mapToObj(i -> new BoostString(boosts[i], strings[i])) // Create the instance
                  .sorted(Comparator.comparingInt(b -> b.boost))         // Sort using a Comparator
                  .map(b -> b.str)                                       // Map it back to a string
                  .toArray(String[]::new);                               // And return an array
          

          上面示例中的Comparator 是使用Comparator.comparingInt 方法创建的,这是使用Java 8 为整数创建Comparator 的便捷方法。


          说明:通常在比较 Java 中的对象时,您会使用内置排序函数之一,例如 Collections.sort,您可以在其中提供自己的 ComparatorComparator 界面简单明了,如下所示:

          public interface Comparator<T> {
              int compare(T o1, T o2);
          
              // Other default methods for Java 8
          }
          

          返回值的类型为int,描述为like this in the JavaDoc

          返回负整数、零或正整数,因为第一个参数小于、等于或大于第二个参数。

          当您对Strings 或int(或实际上是Integers)进行排序时,这是开箱即用的,因为它们是Comparable——它们有点内置自然排序和对于Strings,这是按字母顺序排列的,对于Integers,这是按升序排列的(对于Comparable,请参见the JavaDoc)。

          附带说明,如果您使用的是 3rd 方库,还有其他可用的“对”或“元组”实现。您不必创建自己的“对”Stringint。一个例子是来自Apache CommonsPair 类。

          【讨论】:

            【解决方案8】:

            您可以在旧式 Java 中执行类似于您的 JS 示例的操作(但我建议您按照 @wassgren 的建议将您的数据连接到一个对象中):

            import java.util.*;
            
            public class WeightSort {
              public static void main(String[] args) {
                String[] strings = new String[]{"string1", "string2", "string3"};
                final int[] weights = new int[]{40, 32, 34};
                final List<String> stringList = Arrays.asList(strings);
                List<String> sortedCopy = new ArrayList<String>(stringList);
                Collections.sort(sortedCopy, new Comparator<String>(){
                    public int compare(String left, String right) {
                      return weights[stringList.indexOf(left)] - weights[stringList.indexOf(right)];  
                    }
                  });
                  System.out.println(sortedCopy);
              }
            }
            

            【讨论】:

              【解决方案9】:
              package com.appkart.array;
              
              import java.util.Comparator;
              import java.util.HashMap;
              import java.util.Map;
              import java.util.TreeMap;
              
              public class SortExample {
              
                  Map<String, Integer> map = new HashMap<String, Integer>();
                  Map<String, Integer> treemap = new TreeMap<String, Integer>(
                          new MyComparator(map));
              
                  public void addValueInMapAndSort() {
                      map.put("string1", 40);
                      map.put("string2", 32);
                      map.put("string3", 34);
              
                      System.out.println(map);
                      treemap.putAll(map);
                      System.out.println(treemap);
                  }
              
              
                  class MyComparator implements Comparator<String> {
              
                      Map<String, Integer> map;
              
                      public MyComparator(Map<String, Integer> map) {
                          this.map = map;
                      }
              
                      @Override
                      public int compare(String o1, String o2) {
                          if (map.get(o1) >= map.get(o2)) {
                              return 1;
                          } else {
                              return -1;
                          }
                      }
                  }
              
                  public static void main(String[] args) {
                      SortExample example = new SortExample();
                      example.addValueInMapAndSort();
                  }
              }
              

              使用 Comparator 按值排序。

              【讨论】:

                【解决方案10】:

                创建一个TreeMap&lt;Integer, List&lt;ObjectTypeFromA&gt;&gt;,其中映射键是 B 中的值,映射值是 A 中的值(使用列表允许重复键)。会按照定义按照B的顺序排序。

                public static void main(String[] args) {
                  String[] strings = { "string1", "string2", "string3", "string4" };
                  int[] ints = { 40, 32, 32, 34 };
                  System.out.println(Arrays.toString(getSortedStringArray(strings, ints)));
                }
                
                public static String[] getSortedStringArray(String[] strings, int[] order) {
                  Map<Integer, List<String>> map = new TreeMap<>();
                  for (int i = 0; i < strings.length; i++) {
                    if (!map.containsKey(order[i])) {
                      map.put(order[i], new LinkedList<String>());
                    }
                    map.get(order[i]).add(strings[i]);
                  }
                  String[] ret = new String[strings.length];
                  int i = 0;
                  for (Map.Entry<Integer, List<String>> mapEntry : map.entrySet()) {
                    for (String s : mapEntry.getValue()) {
                      ret[i++] = s;
                    }
                  }
                  return ret;
                }
                

                【讨论】:

                • 如果 B 有重复项怎么办?
                • 我有重复问题。树形图在这里不起作用。
                • 然后将值设为对象的集合,并创建一个包装器,当按顺序迭代树时,它也会遍历每个集合的内容。我会尽快更新答案。您可能应该更新问题以指定允许重复;你的例子并没有让人想到这种可能性。
                • @Reza 更新了答案,展示了这个概念在重复键和稍作修改的情况下仍然可以正常工作。
                • @DeepakTiwari 更新的答案处理重复项,请参阅int[] ints = { 40, 32, 32, 34 };
                【解决方案11】:

                如果您构建的数组 B 仅用于此排序,您可以推迟在 A 的 compareTo() 中计算它的值。换句话说,只在排序期间的比较中计算字符串的权重。

                【讨论】:

                  猜你喜欢
                  • 2022-12-18
                  • 2015-08-05
                  • 2023-02-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多