繁体   English   中英

Java - 根据另一个数组的值对一个数组进行排序?

[英]Java - Sort one array based on values of another array?

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

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

答: [string1, string2, string3]

B: [40, 32, 34]

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

[string2, string3, string1]

在 javascript 你可以这样做:

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

在 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)]));

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

public class BoostString {
    int boost;
    String str;

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

然后,您可以使用Comparator器对数组进行排序,它与 Java 8 流式处理 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您提供自己的Comparator Comparator界面简单明了,如下所示:

public interface Comparator<T> {
    int compare(T o1, T o2);

    // Other default methods for Java 8
}

返回值是int类型,在 JavaDoc 中是这样描述的:

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

当您对Stringint (或实际上是Integer )进行排序时,这可以开箱即用,因为它们是Comparable ——它们有一种内置的自然排序,对于String来说,这是按字母顺序排列的,对于Integers这是按升序排列的(参见 JavaDoc for Comparable )。

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

您可以在旧式 Java 中执行类似于您的 JS 示例的操作(但我建议您按照@wassgren 的建议将您的数据合并到 object 中):

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

我通过使用 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;
     }
 }

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

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

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

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

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 根据值进行排序。

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

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

}

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

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

在 java 中,您需要有两个 arrays 一个副本来排序和要排序的数组。

使用 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)]));

创建一个长数组,前 32 位是排序整数,后 32 位是数组索引。 对该数组进行排序,然后使用现在排序的索引来构建一个排序的字符串数组。

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

    final long[] longs = new long[ints.length];
    for (int i = 0; i < ints.length; i++) {
        longs[i] = (long)ints[i] << 32 | (long)i;
    }
    Arrays.sort(longs);

    String[] sortedStrings = new String[strings.length];
    for(int i = 0; i < longs.length; i++) {
        sortedStrings[i] = strings[(int)longs[i]];
    }

    System.out.println(Arrays.asList(sortedStrings));

我相信这在算法上与上述 Ofek 的基于流的解决方案相同,但使用更传统的 Java。

该算法的一个特点是,如果两个条目具有相同的排序 integer,它们将保留彼此之间的原始序列。

制作一个TreeMap<Integer, List<ObjectTypeFromA>> ,其中 map 键是 B 中的值,map 值是 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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM