JavaRush /Java блог /Архив info.javarush /10 ошибок зачастую допускаемых Java разработчиками
theGrass
24 уровень
Саратов

10 ошибок зачастую допускаемых Java разработчиками

Статья из группы Архив info.javarush
10 ошибок зачастую допускаемых Java разработчиками - 1
Этот список включает 10 ошибок зачастую допускаемых Java разработчиками.
  1. Преобразование массива в ArrayList.

    Для преобразования массива в ArrayList, разработчики часто используют такой способ:

    
    List<String> list = Arrays.asList(arr);
    

    Arrays.asList() вернет объект класса ArrayList который является внутренним приватным статическим классом (private static class) класса Arrays, а это не класс java.util.ArrayList. Класс java.util.Arrays.ArrayList содержит методы set(), get(), contains(), но не содержит никаких методов для добавления элементов, его размер фиксирован. Для создания настоящего java.util.ArrayList следует сделать так:

    
    ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));
    

    Конструктор класса java.util.ArrayList может принимать в качестве параметра все объекты реализующие интерфейс Collection, реализацию которого унаследовал и класс java.util.Arrays.ArrayList

    (private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable).

  2. Проверка массива на наличие конкретного значения.

    Разработчики часто поступают так:

    
    Set<String> set = new HashSet<String>(Arrays.asList(arr));
    return set.contains(targetValue);
    

    Код получается рабочий, но нет необходимости преобразовывать List в Set. Преобразование в Set займет дополнительное время. На самом же деле все просто:

    
    Arrays.asList(arr).contains(targetValue);
    

    или

    
    for(String s: arr){
    	if(s.equals(targetValue))
    		return true;
    }
    return false;
    

    Первый способ значительно короче.

  3. Удаление элемента из List в цикле

    Рассмотрим следующий код, удаляющий элементы в цикле:

    
    ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
    for (int i = 0; i < list.size(); i++) {
    	list.remove(i);
    }
    System.out.println(list);
    

    Вывод:

    
    [b, d]
    

    Получается серьезная ошибка. Когда элемент удален, размер List уменьшается и индексы элементов меняются.

    Так что откажитесь от такого способа, если вы хотите удалить несколько элементов в цикле используя индекс.

    Возможно вы знаете что использование итератора правильное решение для удаления элементов в цикле, и вы знаете что цикл в стиле for-each работатет как итератор, но это не так.

    Рассмотрим следующий код:

    
    ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
     
    for (String s : list) {
    	if (s.equals("a"))
    		list.remove(s);
    }
    

    Мы получим исключение ConcurrentModificationException.

    Правильно поступить так:

    
    ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
    Iterator<String> iter = list.iterator();
    while (iter.hasNext()) {
    	String s = iter.next();
     
    	if (s.equals("a")) {
    		iter.remove();
    	}
    }
    

    Метод next() должен быть вызван до метода remove().

    В цикле в стиле for-each, компилятор вызовет метод remove(), а уже потом next(), что вызовет ошибку ConcurrentModificationException. Вы можете посмотреть код ArrayList.iterator().

  4. Hashtable против HashMap.

    Из-за соответствующей реализации, Hashtable — это название структуры данных.

    Но в Java название для структуры данных это HashMap. Одно из ключевых отличий между Hashtable и HashMap это то, что Hashtable синхронизирован.Так что не стоит использовать Hashtable там где больше подходит HashMap.

    HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap.

    10 основных вопросов про интерфейс Map

  5. Использование коллекций без ограничений по содержимому

    В Java часто путают коллекции без ограничений по содержимому, и коллекции с маской по типу содержимого. К примеру, для множеств - Set это коллекция без ограничений по содержимому, а Set<?> — коллекция у которой все-таки есть ограничения, но эти ограничения ничего на самом деле не ограничивают. Рассмотрим следующий код, где List без ограничений используется в качестве параметра метода:

    
    public static void add(List list, Object o){
    	list.add(o);
    }
    public static void main(String[] args){
    	List<String> list = new ArrayList<String>();
    	add(list, 10);
    	String s = list.get(0);
    }
    

    Данный код выбросит исключение:

    
    Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    	at …
    

    Использование коллекций без ограничений по содержимому очень опасно, потому что вы не можете быть уверенными в том что там лежит внутри. А для того чтобы понять всю глубину разницы между Set, Set<?> и Set<Object> — почитайте вот эту и эту ссылки.

  6. Уровень доступа

    Очень часто для полей класса разработчики используют модификатор доступа public. Так проще получить значение поля напрямую. Правильнее использовать как можно более ограниченный доступ к членам класса.

    public, default, protected, and private.

  7. ArrayList против LinkedList

    Когда разработчики не знают чем отличается ArrayList от LinkedList, они используют первый в силу его большей известности. Однако, есть огромная разница в производительности между ними. На самом деле, выбор между ними должен быть продиктован их внутренними особенностями — ArrayList позволяет быстро производить доступ к произвольному элементу массива, а LinkedList — быстро добавлять/удалять элементы в массиве. Почитайте статью по ссылке ArrayList vs. LinkedList чтобы понять причины их разной производительности.

  8. Mutable (Изменяемый) против Immutable (Неизменяемый)

    Неизменяемый объект имеет много преимуществ: простота, безопасность и т.д. Но он требует отдельного объекта для каждого нового значения, и за слишком большое количество объектов придется заплатить понижением производительности. Должен быть баланс при выборе между изменяемым и неизменяемым обьектом.

    В основном чтобы избежать подготовки промежуточных объектов используется изменяемый объект. Один из классических примеров это конкатенация большого количества строк. Если вы используете неизменяемый объект типа String, то вы создаете много объектов, которые сразу попадут в сборщик мусора. Это тратит время и энергию процессора, поэтому правильное решение это использование изменяемых обьектов (например StringBuilder).

    
    String result="";
    for(String s: arr){
    	result = result + s;
    }
    

    Еще один пример использования изменяемых объектов — передача такого объекта в метод. Это позволит вернуть результат в нем же, без создания лишних объектов. При работе с объектами большого размера, к примеру коллекциями, передача коллекции в метод для сортировки и возвращение результата в другой коллекции приводит к совершенно излишнему расходу ресурсов. (Ответ пользователя dasblinkenlight на Stack Overflow).

    Почему объект класса String неизменяем?

  9. Конструкторы классов Super и Sub

    10 ошибок зачастую допускаемых Java разработчиками - 2

    Данная ошибка компиляции вызвана тем, что у класса-предка не определен конструктор по умолчанию. В Java, если вы сами не опишите конструктор класса, компилятор сам создаст конструктор по умолчанию, не требующий никаких аргументов. Если же конструктор описан в классе Super, как Super(String s){}, компилятор сам ничего добавлять не будет. Что мы и наблюдаем в нашем примере.

    Конструктор класса Sub, не важно какой из них, вызовет конструктор по умолчанию класса Super, так как не указано ничего другого. Поскольку конструктора по умолчанию в классе Super нет — это приведет к ошибке компиляции.

    Первый вариант решения данной проблемы — добавить конструктор по умолчанию в класс Super

    
    public Super(){
        System.out.println("Super");
    }
    

    Второй вариант — удалите описанный нами конструктор из класса Super, чтобы компилятор создал конструктор по умолчанию.

    И последний вариант — добавьте вызов super(value) в конструкторы класса Sub, чтобы вместо конструктора по умолчанию вызывался существующий конструктор класса Super

    Constructor of Super and Sub

  10. " " или конструктор?

    Есть два способа создать строку:

    
    //1. использовать двойные кавычки
    String x = "abc";
    

    
    //2. использовать конструктор
    String y = new String("abc");
    

    В чем же между ними разница?

    Вы можете понять это из следующих примеров:

    
    String a = "abcd";
    String b = "abcd";
    System.out.println(a == b);  // True
    System.out.println(a.equals(b)); // True
     
    String c = new String("abcd");
    String d = new String("abcd");
    System.out.println(c == d);  // False
    System.out.println(c.equals(d)); // True
    

    Для того чтобы подробнее узнать о том как строки хранятся в памяти, читайте Create Java String Using "" or Constructor?.

Планы на будущее. Этот список основан на моем анализе огромного количества Open Source проектов с гитхаба, вопросов со Stack Overflow, и популярных запросов в Google. Не берусь доказать что они действительно входят в десятку самых серьезных ошибок, но они на самом деле очень распространены.
Комментарии (19)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
zdRusty Уровень 36
22 февраля 2021
Еще один способ удаления элемента из листа:

List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
List<String> newList = list.stream().filter(x->(!x.equals("b"))).collect(Collectors.toList());
Aleksey Grin Уровень 22
15 декабря 2020
Как себе в закладки статьи добавлять? Если ли что-то народе избранного? списков или коллекций?
Liudmyla Diachenko Уровень 0
4 мая 2020
import java.util.*; import java.lang.*; import java.io.*; class SplitString { ArrayList<String> even = new ArrayList<String>(); ArrayList<String> uneven = new ArrayList<String>(); public void evenOrUneven() { while(aStorage.size() > 0) { String word = aStorage.poll(); if(word.length() % 2 == 0) { even.add(word); System.out.println("Liczba parzysta znaków \"a\": " + word); } else { uneven.add(word); System.out.println("Liczba nieparzysta znaków \"a\": " + word); } } } } class Kodilla{ public static void main (String[] args){ ArrayDeque<String> aStorage = new ArrayDeque<String>(); Random randomGenerator = new Random(); for (int n=0; n<50; n++) { String result = ""; int randomNumber = randomGenerator.nextInt(50); for (int i = 0; i < randomNumber + 1 ; i++){ result = result + "a"; } aStorage.add(result); } SplitString spliter = new SplitString(); spliter.evenOrUneven(aStorage); } } В чем проблема?
Андрей Калинин Уровень 10
26 марта 2020
Не работает почему-то...
3 марта 2020
очень пригодился материал для решения сложных задач 8 уровня, спасибо большое
idimash Уровень 31
24 марта 2015
Скажите пожалуйста, почему у меня не получается перевести строку в коллекцию байтов?
Делаю так:
List<Byte> byteList = new ArrayList<Byte>(Arrays.asList("ABC".getBytes()));
bukkaker Уровень 16
5 февраля 2015
#3. Удаление элемента из List в цикле.
Как вариант:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (int i = list.size(); i > 0; i--) {
        list.remove(i);
}
System.out.println(list)
bskydive Уровень 22
22 июля 2014
А есть статья про наиболее частые ошибки в решении заданий JavaRush?
blacky Уровень 23
28 июня 2014
Надо бы добавить комментарии правильно/неправильно в конец примеров кода. Или выделить красным, или зачеркнуть неправильный вариант.
А то всегда найдется тот, кто не будет вчитываться, а скопипастит сразу же))
Да читабельность улучшитья — т.к. статья по существу о коде.
List<String> list = Arrays.asList(arr); //неправильно!

List<String> list = Arrays.asList(arr); //неправильно!

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr)); //правильно!