Search code examples
javasortingcollectionscomparatorcopyonwritearraylist

performing sort operation on CopyOnArrayList results in java.lang.UnsupportedOperationException


To avoid Concurrent thread modification exception i used CopyOnArrayList & later when i tried to sort that list with the help Collection's class sort method that resulted in following Exception:-

Exception in thread "main" java.lang.UnsupportedOperationException

As a result i tried reading javadoc available for CopyOnArrayList

which says

Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.

But what i understand, sorting in any sense requires adding and removing of elements into temporary lists. But why is it refraining to do so. It works on object cloning so does this factor affects in any way.

Test code:

package test;

import java.util.Comparator;

/**
 *
 * @author vaibhav.kashyap
 */
public class Student implements Comparator<Student>{

    private String name = "";
    private int age = -1;

    public Student(){

    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() <o2.getAge() ?-1:o1.getAge()==o2.getAge()?0:1;
    }

}

package test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 *
 * @author vaibhav.kashyap
 */
public class Main {
    List<Student> stuList = new CopyOnWriteArrayList<Student>();
    public static void main(String ar[]){
        List<Student> tempList = new Main().makeList();

        Collections.sort(tempList, new Student());

        for(Student s : tempList){
            System.out.println(s.getAge());
        }
    }

    public List<Student> makeList(){
        Student stu = null;
        Scanner sc = new Scanner(System.in);


        for(int i=0 ; i<5; i++){
         stu = new Student();
         System.out.println("Enter name");
         String name = sc.next();
         stu.setName(name);
         System.out.println("Enter age");
         int age = sc.nextInt();
         stu.setAge(age);
         stuList.add(stu);
        }

        return stuList;
    }
}

Output : run: Enter name a Enter age 12 Enter name b Enter age 10 Enter name c Enter age 05 Enter name d Enter age 100 Enter name e Enter age 01 Exception in thread "main" java.lang.UnsupportedOperationException at java.util.concurrent.CopyOnWriteArrayList$COWIterator.set(CopyOnWriteArrayList.java:1049) at java.util.Collections.sort(Collections.java:221) at test.Main.main(Main.java:23) Java Result: 1 BUILD SUCCESSFUL (total time: 30 seconds)

This concept has always been vague to me. Any detailed explanation will be highly appreciable.


Solution

  • The problem is that Collections.sort() works behind the scenes by getting a ListIterator for the List to be sorted. It then uses that iterator's set() method to make modifications, and as the Javadoc points out, COWIterators don't support modifying operations.

    Why not force CopyOnWriteArrayList to implement its own sorting method? As Joshua Bloch put it in this interesting conversation about the problem of sorting these lists, "It violates compatibility to add a method to a widely implemented interface such as List, so this is not a possibility." Besides, CopyOnWriteArrayLists' iterators aren't allowed to make modifications to the underlying lists (IMHO) because modifications are VERY expensive on these lists. Modifying them as heavily as a sort requires should always be a carefully premeditated decision, which (unfortunately) doesn't appear to be natively supported.