如何在Java中对多个数组进行排序

23

我尝试按字典顺序对三个数组进行排序。这些数组通过一个共同的数组相互关联。如果我演示一下会更容易理解:

int[] record = new int[4];
String [] colors = {"blue", "yellow", "red", "black"};
String [] clothes = {"shoes", "pants", "boots", "coat"};

我希望它们在控制台上打印时,能够像下面一样排成三列:

未排序:

Record  Color   Clothes
0       blue    shoes
1       yellow  pants
2       red     boots
3       black   coat

按颜色排序:

Record  Color   Clothes
3       black   coat
0       blue    shoes
2       red     boots
1       yellow  pants

按服装分类:

Record  Color   Clothes
2       red     boots
3       black   coat
1       yellow  pants
0       blue    shoes

我找到了一篇类似于我情况的答案,但是它比较的是整数而不是字符串,我在使用 compareTo() 方法和 Arrays.sort() 时遇到了困难,无法得到我想要的输出。

希望能得到帮助!


颜色是否与衣服相匹配?也许你可以穿黑色鞋子吗? - gobernador
是的,记录、颜色和衣服都彼此绑定。你只能有记录=3,颜色=黑色,衣服=外套。 - 074Geodude
然后,您应该将它们绑在一个单一的对象中。 - Bharat Sinha
12个回答

12
在某些情况下,仅仅为了排序而创建一个新的类并没有太多意义。
这里提供了一个函数,可以用来对任意数量、任意类型的列表(List<?>)根据一个关键字列表(List<T implements Comparable>)进行排序。请点击此处查看 Ideone 示例。

用法

以下是使用该函数对多个任意类型的列表进行排序的示例:
List<Integer> ids = Arrays.asList(0, 1, 2, 3);
List<String> colors = Arrays.asList("blue", "yellow", "red", "black");
List<String> clothes = Arrays.asList("shoes", "pants", "boots", "coat");

// Sort By ID
concurrentSort(ids, ids, colors, clothes);

// Sort By Color
concurrentSort(colors, ids, colors, clothes);

// Sort By Clothes
concurrentSort(clothes, ids, colors, clothes);

输出:

// Sorted By ID:
ID:      [0, 1, 2, 3]
Colors:  [blue, yellow, red, black]
Clothes: [shoes, pants, boots, coat]

// Sorted By Color:
ID:      [3, 0, 2, 1]
Colors:  [black, blue, red, yellow]
Clothes: [coat, shoes, boots, pants]

// Sorted By Clothes:
ID:      [2, 3, 1, 0]
Colors:  [red, black, yellow, blue]
Clothes: [boots, coat, pants, shoes]

代码

可以在这里找到一个Ideone示例,其中包括参数验证和测试用例。

public static <T extends Comparable<T>> void concurrentSort(
                                        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));
}

注意: 运行时间为 O(mlog(m) + mN),其中 m 是列表的长度,N 是列表的数量。通常情况下,m >> N,因此运行时间不会比仅对键进行排序的时间 O(mlog(m)) 更显著。


“Sort By Clothes” 的调用看起来与 “Sort By ID” 相同。打错字了吗? - intrepidis
我觉得很奇怪这个方法被称为“concurrentSort”,但它并没有使用任何并发排序。这是什么原因? - intrepidis
@ChrisNash 标题中的 concurrent 意味着可以通过单个函数调用对多个 List 进行排序(而不是并行排序),但我可以理解这会让人感到困惑。该函数的主要优点在于它将您在答案中提供的函数进行了泛化,即它可以与任意混合的 List<?> 一起使用,而不仅仅是 List<String> - bcorso

7

由于RecordColorClothes似乎是相关的,我建议将它们移动到一个自定义对象中,例如:

public class ClothesItem {
    int record;
    String color;
    String clothes;
}  

然后,您可以制作不同的 Comparator 来执行不同变体的排序。

如果您需要保留当前具有多个数组的结构,则 @Jherico 在此处提供了一种排序解决方案,该解决方案获取排序后的索引数组,这应该使得获得所需结果变得微不足道。


我认为 OP 想要一种同步排序多个数组的方法,而不是寻找存储数据的替代方案。虽然那也是一个好的解决方案。 - brimborium
2
如果我们看一下提供的例子,"record"值也是相关的,需要排序。所以可能还需要将"id"作为对象的一部分添加到类中(类似于@SiB的答案)。否则,在按颜色或衣服排序后,仍需要更多的工作来维护正确的索引。这是在如果OP选择这种方法而不是你列出的第二种方法的情况下。 - user890904

4
好的,这就是最终形式的样子。
// ColorClothes.java

import java.util.*;


public class ColorClothes
{
public int record;
public String color;
public String clothes;

public static void main(String[] args)
{
    Initialize();
}

public ColorClothes(int record, String color, String clothes)
{
    this.record = record;
    this.color = color;
    this.clothes = clothes;
}

public static void Initialize()
{
    List<ColorClothes> list = new ArrayList();
    list = CreateList();

    Sort(list, "Unsorted", 1);
    Sort(list, "\nSortedByColor", 2);
    Sort(list, "\nSortedByClothes", 3);
    Sort(list, "\nSortedByRecord", 4);
}


public static List<ColorClothes> CreateList()
{
    List<ColorClothes> list = new ArrayList();
    list.add(new ColorClothes(1, "blue  ", "shoes"));
    list.add(new ColorClothes(0, "yellow", "pants"));
    list.add(new ColorClothes(3, "red   ", "boots"));
    list.add(new ColorClothes(2, "black ", "coat"));

    return list;
}

public static void Print(List<ColorClothes> list)
{
    for (ColorClothes item : list)
    {
        System.out.println(item.record + "    " + item.color + "   " + item.clothes);
    }
}

public static void Sort(List<ColorClothes> list, String string, int choice)
{
    System.out.println(string + "\n");

    switch (choice)
    {
    case 1:
        break;
    case 2:
        Collections.sort(list, new ColorComparator());
        break;
    case 3:
        Collections.sort(list, new ClothesComparator());
        break;
    case 4:
        Collections.sort(list, new RecordComparator());
        break;
    }

    Print(list);
}

} // End class.

// ColorComparator.java

import java.util.Comparator;

 class ColorComparator implements Comparator
 {
public int compare(Object str1, Object str2)
{
    String str1Color = ((ColorClothes)str1).color;
    String str2Color = ((ColorClothes)str2).color;

    return str1Color.compareTo(str2Color);

}
}// End class.

// ClothesComparator.java

import java.util.Comparator;


class ClothesComparator implements Comparator
{
public int compare(Object str1, Object str2)
{
    String str1Clothes = ((ColorClothes)str1).clothes;
    String str2Clothes = ((ColorClothes)str2).clothes;

    return str1Clothes.compareTo(str2Clothes);

}
} // End class.

// RecordComparator.java

import java.util.Comparator;


public class RecordComparator implements Comparator 
{
public int compare(Object rec1, Object rec2)
{
    int rec1Rec = ((ColorClothes)rec1).record;
    int rec2Rec = ((ColorClothes)rec2).record;

    if(rec1Rec > rec2Rec)
    {
        return 1;
    }
    else if(rec1Rec < rec2Rec)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}
}// End class.

控制台输出

Unsorted

1    blue     shoes
0    yellow   pants
3    red      boots
2    black    coat

SortedByColor

2    black    coat
1    blue     shoes
3    red      boots
0    yellow   pants

SortedByClothes

3    red      boots
2    black    coat
0    yellow   pants
1    blue     shoes

SortedByRecord

0    yellow   pants
1    blue     shoes
2    black    coat
3    red      boots

严肃点说,我因为发布我的完整代码而被踩了?哈哈。我猜人们不喜欢在这里看到完整的答案。 - 074Geodude
感谢您的帖子 ;) - berserk

1
这里是我如何对两个或多个相同长度的字符串数组进行排序,以使第一个数组按顺序排列,其他数组与该顺序匹配:
public static void order(String[]... arrays)
{
    //Note: There aren't any checks that the arrays
    // are the same length, or even that there are
    // any arrays! So exceptions can be expected...
    final String[] first = arrays[0];

    // Create an array of indices, initially in order.
    Integer[] indices = ascendingIntegerArray(first.length);

    // Sort the indices in order of the first array's items.
    Arrays.sort(indices, new Comparator<Integer>()
        {
            public int compare(Integer i1, Integer i2)
            {
                return
                    first[i1].compareToIgnoreCase(
                    first[i2]);
            }
        });

    // Sort the input arrays in the order
    // specified by the indices array.
    for (int i = 0; i < indices.length; i++)
    {
        int thisIndex = indices[i];

        for (String[] arr : arrays)
        {
            swap(arr, i, thisIndex);
        }

        // Find the index which references the switched
        // position and update it with the new index.
        for (int j = i+1; j < indices.length; j++)
        {
            if (indices[j] == i)
            {
                indices[j] = thisIndex;
                break;
            }
        }
    }
    // Note: The indices array is now trashed.
    // The first array is now in order and all other
    // arrays match that order.
}

public static Integer[] ascendingIntegerArray(int length)
{
    Integer[] array = new Integer[length];
    for (int i = 0; i < array.length; i++)
    {
        array[i] = i;
    }
    return array;
}

public static <T> void swap(T[] array, int i1, int i2)
{
    T temp = array[i1];
    array[i1] = array[i2];
    array[i2] = temp;
}

如果您想对其他类型的数组执行此操作,则需要进行一些重构。或者,为了使整数数组与字符串数组一起排序,您可以将整数转换为字符串。

需要注意的是,为了使用此函数,您应确保输入数组具有相同的长度,不应依赖于异常来处理此问题。 - intrepidis
请注意,它是不区分大小写地比较字符串。 - intrepidis
如果您感兴趣,我已经发布了一个类似的函数答案,可以同时对任意数量的List进行排序,也可以具有任意类型(不仅仅是String)。 - bcorso

1

我不确定一次排序多个数组是否可行;根据您使用的用例,这似乎是一个有竞争力的方案,可以将所有3个属性合并为一个对象,然后对对象数组进行多种方式的排序。

您确定需要有3个数组吗?

ColoredCloth数组这样的数组是否适合您:

class ColoredCloth implements Comparable<ColoredCloth>{
    int id;
    String color;
    String cloth;
}

定义一些比较器,以便按颜色布料进行排序。


1

间接地对数组进行排序。索引所有的数组,仅对所需数组的索引数组进行排序。查看此SO post中的解决方案。这将使您的数组保持一致。我不确定是否容易将其推广到同步排序N个数组,但它应该让您了解如何处理分布在多个数组中的数据。正如一些人已经指出的那样,将数据分组到单个对象中是一个好方法。


0
正如其他人所建议的那样,更容易的方法是对对象集合进行排序,而不是同步排序三个数组。
如果由于某种原因您必须坚持对多个数组进行排序,则可以使用以下方法 - 思路是实现自己的数组列表变体,该变体由三个数组支持而不是一个。
import java.util.AbstractList;
import java.util.Collections;

public class SortMultipleArrays extends AbstractList {

    //object representing tuple from three arrays
    private static class ClothesItem implements Comparable<ClothesItem> {
        int record;
        String color;
        String clothes;

        public ClothesItem(int record, String color, String clothes) {
            this.record = record;
            this.color = color;
            this.clothes = clothes;
        }

        @Override
        public int compareTo(ClothesItem o) {
            return this.color.compareTo(o.color); //sorting by COLOR
        }
    }

    private int[] records;
    private String[] colors;
    private String[] clothes;

    public SortMultipleArrays(int[] records, String[] colors, String[] clothes) {
        this.records = records;
        this.colors = colors;
        this.clothes = clothes;
    }

    @Override
    public Object get(int index) {
        return new ClothesItem(records[index], colors[index], clothes[index]);
    }

    @Override
    public int size() {
        return records.length;
    }

    @Override
    public Object set(int index, Object element) {
        ClothesItem item = (ClothesItem) element;
        ClothesItem old = (ClothesItem) get(index);

        records[index] = item.record;
        colors[index] = item.color;
        clothes[index] = item.clothes;

        return old;
    }

    public static void main(String[] args) {
        int[] record = {0,1,2,3};
        String[] colors = {"blue", "yellow", "red", "black"};
        String[] clothes = {"shoes", "pants", "boots", "coat"};

        final SortMultipleArrays multipleArrays = new SortMultipleArrays(record, colors, clothes);
        Collections.sort(multipleArrays);

        System.out.println("Record  Color   Clothes");
        for (int i = 0; i < record.length; i++) {
            System.out.println(String.format("%8s %8s %8s", record[i], colors[i], clothes[i]));
        }
    }
}

这个实现基于 AbstractList,使得实现 Collections.sort(...) 所需的 List 接口更容易。

请注意,此实现中可能隐藏着低效率:get(...) 和 set(...) 方法都创建了包装对象的实例,当对更大的数组进行排序时,可能会导致创建过多的对象。


0
我建议你创建一个如下的类。
class Dress {
  public int record;
  public String color;
  public String clothes;
}

维护以下服装清单

List<Dress> dressCollection = new ArrayList<Dress>();

基于颜色和衣服实现比较器。

List<Dress> resultBasedOnColor = Collections.sort(dressCollection, new Comparator<Dress>() {
   public int compareTo(Dress obj1, Dress obj2) {
     return obj1.color.compareTo(obj2.color);
 }

});

根据衣服进行左排序,作为问题所有者的练习。


0
将数据放入自定义类中,例如@SiB:
class ColoredClothes {
    int id;
    String color;
    String cloth;
}

然后,将该类的每个实例放入一个TreeMap中,以颜色作为键(或布料名称,取决于您要按什么排序):
TreeMap<String,ColoredClothes> sortedCloth= new TreeMap<String,ColoredClothes>();
//loop through arrays and put new ColoredClothes into Map

然后像这样获取排序后的值:
Collection<ColoredClothes> values = sortedCloth.values();

你可以使用values.iterator()按顺序迭代这些。

0

谢谢大家的帮助。

我一直固定于使用数组和对这些数组进行排序(因为这是我需要做的),以至于我甚至没有考虑过创建对象。

通过这个简单的程序,您可以创建一个对象并对对象中的字段进行排序。颜色和衣服只是我使用的示例。

下面是我的完整代码:

// ColorClothes.java

import java.util.*;


public class ColorClothes
{
public int record;
public String color;
public String clothes;

public static void main(String[] args)
{
    Initialize();
}

public static void Initialize()
{
    ColorClothes item[] = new ColorClothes[4];

    item[0] = new ColorClothes();
    item[0].record = 0;
    item[0].color = "blue";
    item[0].clothes = "shoes";

    item[1] = new ColorClothes();
    item[1].record = 1;
    item[1].color = "yellow";
    item[1].clothes = "pants";

    item[2] = new ColorClothes();
    item[2].record = 2;
    item[2].color = "red";
    item[2].clothes = "boots";

    item[3] = new ColorClothes();
    item[3].record = 3;
    item[3].color = "black";
    item[3].clothes = "coat";

    System.out.println("Unsorted");

    for(int i = 0; i < item.length; i++)
    {
        System.out.println(item[i].record + "     " + item[i].color + "     " + item[i].clothes);
    }

    System.out.println("\nSorted By Color\n");

    Arrays.sort(item, new ColorComparator());

    for(int i = 0; i < item.length; i++)
    {
        System.out.println(item[i].record + "     " + item[i].color + "     " + item[i].clothes);
    }

    System.out.println("\nSorted By Clothes\n");

    Arrays.sort(item, new ClothesComparator());

    for(int i = 0; i < item.length; i++)
    {
        System.out.println(item[i].record + "     " + item[i].color + "     " + item[i].clothes);
    }

}

}// End class.

// ColorComparator.java

import java.util.Comparator;

class ColorComparator implements Comparator
{
public int compare(Object str1, Object str2)
{
    String str1Color = ((ColorClothes)str1).color;
    String str2Color = ((ColorClothes)str2).color;

    return str1Color.compareTo(str2Color);

}
}// End class.

// ClothesComparator.java

import java.util.Comparator;


class ClothesComparator implements Comparator
{
public int compare(Object str1, Object str2)
{
    String str1Clothes = ((ColorClothes)str1).clothes;
    String str2Clothes = ((ColorClothes)str2).clothes;

    return str1Clothes.compareTo(str2Clothes);

}
} // End class.

控制台输出

Unsorted
0     blue     shoes
1     yellow     pants
2     red     boots
3     black     coat

Sorted By Color

3     black     coat
0     blue     shoes
2     red     boots
1     yellow     pants

Sorted By Clothes

2     red     boots
3     black     coat
1     yellow     pants
0     blue     shoes

稍后我会添加另一个比较器以按记录/整数排序。我还会将代码压缩,这样它就不是一个大块,但我今天的工作快要完成了。


很好,你在最后发布了你的问题。接受那个你认为对你最有帮助的答案。 - Bharat Sinha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接