Search code examples
javalistsortingnested-listscomparator

How to Sort a Nested List of strings


I want to sort the list by values in the list. I want to do multisorting based on few parameters in the list. Providing sample example how data looks like.

Note: I don't have feasibility to convert List<List<String>> into a list of objects.

List<List<String>> data = new ArrayList<>();
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();

list1.add("Siva"); 
list1.add("20");
list1.add("Hyd");
list1.add("TA");
list1.add("India");  

list2.add("Suresh");    
list2.add("22"); 
list2.add("Banglore");
list2.add("KA");  
list2.add("India");

list3.add("Ramesh"); 
list3.add("24");
list3.add("Chennai"); 
list3.add("TN");
list3.add("India");

data.add(list1);
data.add(list2);
data.add(list2);

I want to do multi sorting based on name, age and city.

It's just sample data. List of lists is dynamic. Sorting parameters will also change sometimes.

I want to do sorting on a list of lists of strings only.

Expected Output: List<List<String>> sortedData


Solution

  • Solution by Maintaining your Structure

    If you can't really create a class wrapping the data in your nested List (for whatever reason), you could use the collection stream and define the sorted operation's logic as follows:

    List<List<String>> listRes = data.stream()
            .sorted((x, y) -> {
                int res = x.get(0).compareTo(y.get(0));  //Comparing by name
                if (res != 0) return res;
                res = Integer.valueOf(x.get(1)).compareTo(Integer.valueOf(y.get(1))); //Comparing by age (numeric value)
                if (res != 0) return res;
                return x.get(2).compareTo(y.get(2));  //Comapring by city
            })
            .collect(Collectors.toList());
    

    Link to test the code above:

    https://ideone.com/RhW1VI

    Alternative Solution

    However, as it has been pointed out in the comments, a better approach would be to create a custom class representing your data in the nested List. Perhaps a simple record if you're using Java 14 or later with a factory method to retrieve an instance of your class from a nested List.

    Then, with a stream you could map each nested list to your custom class and sort it with a Comparator.

    Here is a snippet of the implementation:

    public static void main(String[] args) {
            List<List<String>> data = /* ... your initialization ... */
    
            List<MyClass> listSorted = data.stream()
                    .map(list -> MyClass.createMyClass(list))
                    .sorted(Comparator.comparing(MyClass::getName).thenComparing(MyClass::getAge).thenComparing(MyClass::getCity))
                    .collect(Collectors.toList());
    
            System.out.println(listSorted);
    }
    

    Mapping record

    record MyClass(String name, int age, String city, String code, String country) {
    
        public static MyClass createMyClass(List<String> list) {
            if (list == null || list.size() < 5) {
                return null;
            }
    
            MyClass mc = new MyClass();
            mc.name = list.get(0);
            mc.age = Integer.valueOf(list.get(1));
            mc.city = list.get(2);
            mc.code = list.get(3);
            mc.country = list.get(4);
    
            return mc;
        }
    }
    

    Here there is also a link with both implementations:

    https://ideone.com/UK9trV