简体   繁体   中英

Sort ArrayList items by name

I am trying to rearrange an ArrayList based on the name of the items to be on specific index.

My list currently is this:

"SL"
"TA"
"VP"
"SP"
"PR"

and i want to rearrange them to:

"SL"
"SP"
"TA"
"PR"
"VP"

but based on the name and not in the index.

I have tried this:

for (int i=0; i< list.size(); i++){
    if (list.get(i).getCategoryName().equals("SL")){
        orderedDummyJSONModelList.add(list.get(i));
    }
}
for (int i=0; i< list.size(); i++){
    if (list.get(i).getCategoryName().equals("SP")){
        orderedDummyJSONModelList.add(list.get(i));
    }
}
for (int i=0; i< list.size(); i++){
    if (list.get(i).getCategoryName().equals("TA")){
        orderedDummyJSONModelList.add(list.get(i));
    }
}
for (int i=0; i< list.size(); i++){
    if (list.get(i).getCategoryName().equals("PR")){
        orderedDummyJSONModelList.add(list.get(i));
    }
}
for (int i=0; i< list.size(); i++){
    if (list.get(i).getCategoryName().equals("VP")){
       orderedDummyJSONModelList.add(list.get(i));
    }
}

and it works fine, but i want to know if there is a more efficient way to do in 1 for loop or maybe a function. I do not wish to do it like this:

orderedDummyJSONModelList.add(list.get(0));
orderedDummyJSONModelList.add(list.get(3));
orderedDummyJSONModelList.add(list.get(1));
orderedDummyJSONModelList.add(list.get(4));
orderedDummyJSONModelList.add(list.get(2));

Which also works. Any ideas?

You can use Collection.Sort method as Collection.Sort(list) since list is a List<String> you will be fine. But if you want to implement a new comparator:

Collections.sort(list, new NameComparator());

class NameComparator implements Comparator<String> { //You can use classes
    @Override
    public int compare(String a, String b) { //You can use classes
        return a.compareTo(b); 
    }
}

EDIT:

You can define a class comparator for your needs:

class ClassComparator implements Comparator<YourClass> { //You can use classes
    @Override
    public int compare(YourClass a, YourClass b) { //You can use classes
        return a.name.compareTo(b.name); 
    }
}

The key thing here is: you need to get clear on your requirements .

In other words: of course one can shuffle around objects stored within a list. But: probably you want to do that programmatically .

In other words: the correct approach is to use the built-in Collection sorting mechanisms, but with providing a custom Comparator.

Meaning: you better find an algorithm that defines how to come from

"SL" "TA" "VP" "SP" "PR"

to

"SL" "SP" "TA" "PR" "VP"

That algorithm should go into your comparator implementation!

The point is: you have some List<X> in the first place. And X objects provide some sort of method to retrieve those strings you are showing here. Thus you have to create a Comparator<X> that works on X values; and uses some mean to get to those string values; and based on that you decide if X1 is <, = or > than some X2 object!

  1. Use a hashmap to store the weight of all strings (Higher the value of the hashmap means the later this string should come in the final list).
  2. Using a Hashmap, so you can expand it later for other strings as well. It'll be easier to enhance in future.
  3. Finally, Use a custom Comparator to do it.

Required Setup:

       List<String> listOfStrings = Arrays.asList("SL", "TA", "VP", "SP", "PR");

        HashMap<String, Integer> sortOrder = new HashMap<>();
        sortOrder.put("SL", 0);
        sortOrder.put("TA", 1);
        sortOrder.put("VP", 2);
        sortOrder.put("SP", 3);
        sortOrder.put("PR", 4);

Streams:

        List<String> sortedList = listOfStrings.stream().sorted((a, b) -> {
            return Integer.compare(sortOrder.get(a), sortOrder.get(b));
        }).collect(Collectors.toList());

        System.out.println(sortedList);

Non-Stream:

        Collections.sort(listOfStrings, (a, b) -> {
            return Integer.compare(sortOrder.get(a), sortOrder.get(b));
        });
OR
        listOfStrings.sort((a, b) -> {
            return Integer.compare(sortOrder.get(a), sortOrder.get(b));
        });

        System.out.println(listOfStrings);

Output :

[SL, TA, VP, SP, PR]

here´s an answer just specific for your problem working just for the given output. If the List contains anything else this might break your ordering, as there is no rule given on how to order it and the PR just randomly appears in the end.

public static void main(String[] args) {
    List<String> justSomeNoRuleOrderingWithARandomPRInside = new ArrayList<String>();
    justSomeNoRuleOrderingWithARandomPRInside.add("SL");
    justSomeNoRuleOrderingWithARandomPRInside.add("TA");
    justSomeNoRuleOrderingWithARandomPRInside.add("VP");
    justSomeNoRuleOrderingWithARandomPRInside.add("SP");
    justSomeNoRuleOrderingWithARandomPRInside.add("PR");
    java.util.Collections.sort(justSomeNoRuleOrderingWithARandomPRInside, new NameComparator());
    for(String s : justSomeNoRuleOrderingWithARandomPRInside) {
        System.out.println(s);
    }
}

static class NameComparator implements Comparator<String> { //You can use classes
    @Override
    public int compare(String a, String b) { //You can use classes
        // Lets just add a T in front to make the VP appear at the end 
        // after TA, because why not
        if (a.equals("PR")) {
            a = "T"+a;
        } else if(b.equals("PR")) {
            b = "T"+b;
        }
        return a.compareTo(b);
    }
}

O/P

SL
SP
TA
PR
VP

But honestly, this solution is crap, and without any clear rule on how to order these this will be doomed to fail as soon as you change anything as @GhostCat tried to explain.

How about this

// define the order
List<String> ORDER = Arrays.asList("SL", "SP", "TA", "PR", "VP");

List<MyObject> list = ...
list.sort((a, b) -> {
    // lamba syntax for a Comparator<MyObject>
    return Integer.compare(ORDER.indexOf(a.getString()), ORDER.indexOf(b.getString());
});

Note that this will put any strings that aren't defined in the ORDER list at the start of the sorted list. This may or may not be acceptable - it may be worth checking that only valid strings (ie members of ORDER) appear as the result of MyObject.getString().

You can build an index map using a LinkedHashMap . This will be used to lookup the order which to sort using the category names of your items.

ItemSorting

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

public class ItemSorting {
    public static void main(String[] args) {
        List<Item> list = new ArrayList<Item>();
        IndexMap indexMap = new IndexMap("SL", "SP", "TA", "PR", "VP");
        ItemComparator itemComparator = new ItemComparator(indexMap);

        list.add(new Item("SL"));
        list.add(new Item("TA"));
        list.add(new Item("VP"));
        list.add(new Item("SP"));
        list.add(new Item("PR"));

        Collections.sort(list, itemComparator);

        for (Item item : list) {
            System.out.println(item);
        }
    }
}

ItemComparator

import java.util.Comparator;

public class ItemComparator implements Comparator<Item> {
    private IndexMap indexMap;

    public IndexMap getIndexMap() {
        return indexMap;
    }

    public void setIndexMap(IndexMap indexMap) {
        this.indexMap = indexMap;
    }

    public ItemComparator(IndexMap indexMap) {
        this.indexMap = indexMap;
    }

    @Override
    public int compare(Item itemA, Item itemB) {
        if (itemB == null) return -1;
        if (itemA == null) return 1;
        if (itemA.equals(itemB)) return 0;

        Integer valA = indexMap.get(itemA.getCategoryName());
        Integer valB = indexMap.get(itemB.getCategoryName());

        if (valB == null) return -1;
        if (valA == null) return 1;

        return valA.compareTo(valB);
    }
}

IndexMap

import java.util.LinkedHashMap;

public class IndexMap extends LinkedHashMap<String, Integer> {
    private static final long serialVersionUID = 7891095847767899453L;

    public IndexMap(String... indicies) {
        super();

        if (indicies != null) {
            for (int i = 0; i < indicies.length; i++) {
                this.put(indicies[i], new Integer(i));
            }
        }
    }
}

Item

public class Item {
    private String categoryName;

    public Item(String categoryName) {
        super();
        this.categoryName = categoryName;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((categoryName == null) ? 0 : categoryName.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Item other = (Item) obj;
        if (categoryName == null) {
            if (other.categoryName != null) return false;
        } else if (!categoryName.equals(other.categoryName)) return false;
        return true;
    }

    @Override
    public String toString() {
        return String.format("Item { \"categoryName\" : \"%s\" }", categoryName);
    }
}

Result

Item { "categoryName" : "SL" }
Item { "categoryName" : "SP" }
Item { "categoryName" : "TA" }
Item { "categoryName" : "PR" }
Item { "categoryName" : "VP" }

You can create a Map that maintains the position. When you iterate through the unordered list just get the position of that string value and insert into new array(not arraylist), then later if required you can convert that array to ArrayList. Example code:

Map<String,Integer> map = new HashMap<>(); //you can may be loop through and make this map
map.put("SL", 0);
map.put("SP", 1);
map.put("TA",2);
map.put("PR",3);
map.put("VP",3);
List<String> list1 // your unordered list with values in random order
String[] newArr =  new String[list1.size()];
for(String strName: list1){
    int position  = map.get(strName);
    arr[position] = strName;
}
//newArr has ordered result.

You coud define a helper method like this one:

public static int get(String name) {
    switch (name) {
    case "SL":
        return 1;
    case "SP":
        return 2;
    case "TA":
        return 3;
    case "PR":
        return 4;
    case "VP":
        return 5;
    default:
        return 6;
    }
}

and write in your main method something like:

ArrayList<String> al = new ArrayList<>();
al.add("SL");
al.add("TA");
al.add("VP");
al.add("SP");
al.add("PR");
Collections.sort(al, (o1, o2) -> return get(o1) - get(o2); );
al.forEach((s) -> System.out.println(s));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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