Пользователь Vonorim
Vonorim
26 уровень

Подробный разбор класса ArrayList [Часть 2]

Статья из группы Java Developer
К сожалению, в одну статью вся информация не поместилась, поэтому продолжаем разбираться с оставшимися методами класса ArrayList. Отсортировать коллекцию:


public void sort(Comparator< ? super E> c)
Осуществляет сортировку списка по заданному правилу. Правило сортировки представляет собой реализованный интерфейс Comparator с переопределенным методом compare(). Переопределение нужно, если коллекция содержит объекты собственного класса. При работе со стандартными классами (Integer, String и так далее) переопределение compare обычно требуется только для нестандартной сортировки. Создадим класс Student:

class Student{
 String student_name;
 int id;
  
 Student(int id, String  student_name){
  this.id = id;
  this.student_name = student_name;
 } 
 
 public String toString(){
  return id + " " + student_name;
 }
}
Напишем простой компаратор, который будет сравнивать id студентов:

class StudentIdComparator implements Comparator<student>{

 public int compare(Student e1, Student e2) {
  return e1.id.compareTo(e2.id);
 }
}
Создадим список для студентов и объект класса, реализующего Comparator:

ArrayList<Student> myList = new ArrayList<> ();
StudentIdComparator comparator = new StudentIdComparator();
Вызовем метод sort для нашего списка и передадим в него компаратор:

myList.sort(comparator);
В результате исходный список [4 David, 2 Tom, 5 Rohit, 1 Paul, 3 Vishal] превратится в [1 Paul, 2 Tom, 3 Vishal, 4 David, 5 Rohit]. Напоследок я оставил очень интересный, но редко кем используемый метод:

public List<E> subList(int fromIndex, int toIndex)
Он возвращает не новый список, как может показаться, а вид (view) списка (подсписок), для которого этот метод был вызван, таким образом, что оба списка станут разделять хранимые элементы. subList — полнофункциональный список, он работает и на запись, внося соответствующие изменения в родительский список. Из этого вытекают прекрасные свойства:

someList.subList(3, 7).clear(); 
В данном примере из списка someList будут удалены четыре элемента, с третьего по седьмой (не включительно). Указываем диапазон для работы со списком и вперед. Внутри метода, по сути, происходит обращение к классу SubList, у которого есть собственные реализации известных методов, а в результате работы метода возвращается объект этого класса. Саму реализацию класса можно посмотреть в исходном коде. Для закрепления материала я предлагаю Вам написать свою реализацию динамического массива. Это будет очень полезно в будущем. В качестве примера я представляю свою реализацию динамического массива только для чисел с комментариями в коде.

public class IntegerArrayList {

    private int [] elements;  //массив, для хранения чисел
    private int size;  //поле-счетчик, которое указывает на количество элементов в массиве
    private static final int DEFAULT_CAPACITY = 10;  //размер массива по умолчанию

    //конструктор без параметров, который создает массив на 10 элементов, если размер не был указан
    public IntegerArrayList(){  // 
        this.elements = new int[DEFAULT_CAPACITY];
    }

    //создает массив указанной емкости
    public IntegerArrayList(int initialCapacity){
        if (initialCapacity >= 0){  
            this.elements = new int[initialCapacity]; 
        }
        else {
            throw new IllegalStateException("Capacity can't be less than 0!");
        }
    }

    //получает элемент по указанному индексу
    public int get(int index){
        isIndexExist(index);  //проверка корректности введенного индекса
        return elements[index];
    }

    //возвращает количество элементов в списке
    public int size (){
        return size;
    }

    //добавляем элемент в конец списка
    public boolean add(int value){
        if (size == elements.length){  //если в массиве места нет 
            elements = increaseCapacity(); //вызываем метод, который отвечает за увеличение массива
        }
        elements[size] = value; //записываем в конец списка новое значение
        size++;  //увеличиваем значение переменной размера списка
        return true;
    }

    //дополнительный закрытый метод для увеличения емкости массива 
    private int [] increaseCapacity(){
        int [] temp = new int[(elements.length * 2)];  //создаем новый массив большего размера 
        System.arraycopy(elements, 0, temp, 0, elements.length);  //копируем в новый массив элементы из старого массива
        return temp;
    }

    //устанавливает элемент на указанную позицию
    public int set(int value, int index){
        isIndexExist(index);
        int temp = elements[index];
        elements[index] = value;
        return temp;
    }

    //переопределил метод для красивого вывода списка на экран, иначе будут выводиться значения незаполненных ячеек [1, 10] вместо [1, 10, 0, 0...]
    @Override
    public String toString(){
        int [] temp = new int[size];
        System.arraycopy(elements, 0, temp, 0, size);
        return Arrays.toString(temp);
    }

    //проверяем индексы, не выходят ли они за границы массива
    private int isIndexExist(int index){
        if (index >= size || index < 0){
            throw new IndexOutOfBoundsException("Element can't be found! "
                    + "Number of elements in array = " + size
                    + ". Total size of array = " + elements.length);
        }
        return index;
    }

    //проверяем, есть ли элементы в списке
    public boolean isEmpty(){
        return (size == 0);
    }

    //удаляем элемент по индексу
    public int remove (int index){
        isIndexExist(index);  //проверяем индекс
        int [] temp = elements;  //во временный массив заносим ссылку на текущий массив
        elements = new int [temp.length-1];  //полю elements присваиваем ссылку на новый массив размером меньше на 1
        int value = temp[index];  //сохраняем в доп. переменную значение удаляемого элемента
        System.arraycopy(temp, 0, elements, 0, index);  //копируем левую часть массива до указанного индекса 
        System.arraycopy(temp, index + 1, elements, index, temp.length - index - 1);  //копируем правую часть массива после указанного индекса
        size--;  //уменьшаем значение переменной
        return value;
    }
}
Подробный разбор класса ArrayList [Часть 2] - 1Список использованных источников:
  1. Исходный код ArrayList (актуальность - JDK 12);
  2. Большинство иллюстраций брал отсюда и некоторых статей с JavaRush;
  3. Статья на хабре.
Комментарии (3)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
Людмила 18 уровень, Харьков
9 апреля 2021
Вау! Спасибо за обе статьи! И отдельная благодарность за пример написания реализации! Очень круто!!!!
Sandra Rigaa 8 уровень, Garching
14 января 2021
There is mistake "Напишем простой компаратор, который будет сравнивать id студентов:"

class StudentIdComparator implements Comparator<student>{

 public int compare(Student e1, Student e2) {
  return e1.id.compareTo(e2.id);
 }
}
return e1.id.compareTo(e2.id); You cant use method copareTo to compare two int, this method can use to compare Strings This line must look like this return e1.student_name.compareTo(e2.student_name); Found out only because tray to write this code in InteliJ, and InteliJ shows misstate so try to read first source of this example - > https://www.geeksforgeeks.org/comparator-interface-java/
Игорь Birt 24 уровень, Тирасполь Expert
10 апреля 2020
Довольно сложно к пониманию...(7 уровень JR)