• ,

Объекты классов и коллекции. Непонятки.

Во время обучения Java возникает куча вопросов, некоторые решаются, а некоторые так и остаются не решенными. Вот и у меня возникла пара вопросов.

1. Вопрос по объектам класса и их объявлению.

Чем такая запись
Cat cat = new Cat();

отличается от такой?
new Cat();


Правильно ли я понимаю, что если я напишу так:
Cat Murka = new Cat();

то это будет равносильно следующей записи:
new Cat(Murka);


А если я напишу вот так?
Cat Murka = new Cat(Barsik);

Что произойдет в этом случае? Я сделаю копию объекта Barsik и запишу его в объект Murka? Верно?

2. Вопрос по Коллекциям

Какая разница между записями? В чем принципиальное отличие?

Map<String,String> map1 = new HashMap<->();


HashMap<String,String> map2 = new HashMap<->();
  • ,

Double brace инициализация

1. Double brace инициализация


Перевод статьи What is Double Brace Initialization in Java? Anti Pattern Example (опубликованной в октябре 2015 года).

Инициализация с использованием двойных фигурных скобок (далее по тексту — Double brace инициализация) – Java средство для создания коллекций таких как list, set и map одновременно с их объявлением.
Когда вам необходимы списки с фиксированными элементами, такие как перечень поддерживаемых продуктов или денежных единиц, то объявление списка одновременно с его инициализацией улучшает читабельность кода. Именно поэтому Double brace инициализация набирает популярность, так как иных стандартных способов создания с одновременной инициализацией в коде для коллекций нет.
К сожалению, в отличие от других языков программирования Java не поддерживает коллекции литералов. В соответствие с данным ограничением, создание неизменяемого списка (unmodifiableList) даже с небольшим количеством элементов вынуждает нас писать много строк кода в виде повторяющихся вызовов add() для добавления нужных элементов с завершающим обертыванием (wrapping):
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(5);
list.add(7);        
List<Integer> unmodifiableList = Collections.unmodifiableList(list);


Это излишне избыточное описание, которое можо упростить. Заполним статические списки удобным для нас способом, а именно:
непосредственно в статических блоках при инициализации, в чем нам и поможет Double brace инициализация, позволяющая нам записать все в одну строку:
List<Integer> list = Collections.unmodifiableList(new ArrayList<Integer>() {{
        add(2);
        add(3);
        add(5);
}});


Аналогично Double brace инициализация поможет нам заполнить значениями и HashMap:
Map<Integer, String> intToString = new HashMap<Integer, String>(){{
         put(1, "one");
         put(2, "two");
         put(3, "three");         
 }};


Все это так элегантно выглядит, однако имеет свои недостатки, которые и делают Double brace инициализацию анти-шаблоном. Рассмотрим их далее в следующей главе.

Плюсы и минусы Double brace инициализации.


Double brace инициализация использует создание внутреннего анонимного класса (anonymous inner class). Что поначалу внешне скрыто, однако Double brace инициализация создает класс с дальнейшей инициализацией его экземпляра, каждый раз как вы применяете ее. Помимо этого используется скрытая ссылка на этот закрытый класс, что может привести нас к возможным утечкам памяти. Вы также не сможете использовать оператор привидения для generic-ов (diamond operator < >), так как мы не можем обратиться внутрь анонимного класса.
(От переводчика: Еще раз более подробно:
после первой { создается внутренний анонимный класс,
после второй { происходит инициализация при создании экземпляра класса при которой мы имеем доступ к полям и методам внешнего (по отношению к анонимному) класса.)


Плюсы:
  1. Уменьшение строк в коде
  2. Создание и инициализация в одном выражении.
Минусы:
  1. Скрытое от вас создание анонимного класса.
  2. Что стоит нам дополнительных расходов для его экземпляра каждый раз при использовании.
  3. Каждый раз создается скрытая ссылка на него, которая возможно приведет к утечкам памяти.

Вердикт:
Из-за вышеуказанных минусов и существованию альтернатив Double brace инициализация рассматривается как анти-шаблон в мире Java.
Спасите котенка

Альтернативы Double brace инициализации в Java.


Хорошая новость состоит в том, что имеются иные способы достижения тех же самых целей в Java. Мы может реализовать в одной строке кода создание и инициализацию значениями ArrayList используя Copy constructor из Collection класса как показано ниже:
List<Integer> list = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(2, 3, 5)));


Arrays.asList() возвратит нам список фиксированной длины переданный в ArrayList copy constructor. Помните про различие между списками фиксированной длины возвращаемыми из Arrays.asList() и Collections.unmodifiableList():
Вы не можете добавлять или удалять элементы ArrayList-а, однако вы можете изменить элемент по индексу с использованием set(), чего вам не удастся сделать со списком, возвращенным Collections.unmodifiableList().
Если вы хотите получить небольшой список – это лучший способ, хотя он будет менее прозрачным для Set и других коллекций, так придется создать List перед созданием Set-а. Но это все же лучше, нежели Double brace инициализация, так как в данном случае не создается лишний внутренний анонимный класс при каждом подобном использовании.

Если вы работаете под Java 8 у вас есть еще один альтернативный способ. JDK 8 Stream API поможет вам создавать небольшие коллекции комбинируя вывод Stream Factory методы в коллекцию List:
List<String> list = Collections.unmodifiableList(Stream.of("abc", "bcd", "cde").collect(toList()));


Для Set вы можете использовать Collectors.toSet() метод вместо Collectors.toList(), как показано ниже:
Set<String> set = Collections.unmodifiableSet(Stream.of("abc", "bcd", "cde").collect(toSet()));


Кстати, Stream collect методы не гарантируют, что сгенерированные ими коллекции защищены от изменений. В Java 8 коллекции, которые они вернули (такие как — ArrayList, HashSet, and HashMap ), вполне себе обычные ( мы можем менять их), но это данный факт возможно будет исправлен в будущих релизах JDK.

Это все на данный момент о Double brace инициализации в Java. Этот шаблон приемлим для тестов и демонстраций, но не достаточно хорош для использования в промышленной эксплуатации (production). Из-за присущих ему минусов, Double brace инициализация стал анти-шаблоном в наши дни, особенно учитывая доступные альтернативы. Сам я все еще использую данную конструкцию для инициализации static map-ов и все. Для List я предпочитаю создавать Collections комбинируя с созданием Array.asList в его конструкторе. А если использую Java 8 – конструкцию с использованием Stream API и collect().

Статьи по теме:
Если вам понравился данный учебный материал и вы хотите узнать побольше о паттернах, принципах и лучших приемах программирования на Java, возможно вы также ознакомитесь и с другими статьями на нашем сайте.

Рекомендуемая литература:
Если вы хотите побольше узнать о шаблонах и лучших практиках вы должны прочитать книгу «Эффективное программирование» Джошуа Блоха, ни одна книга не может занять ее место.
А если вы уже поднаторели в Java и ищете книгу по шаблонам проектирования, юморной стиль изложения которой интересно и легко читать – обратите внимание на «Нead First. Шаблоны проектирования».
От переводчика: Я умышленно привел ссылку на оригинальную книгу Блоха, так как перевод ее на русский неудачен (например, Builder=конструктор).

Как добавить элементы в абстрактную коллекцию ?

Как добавить элементы в абстрактную коллекцию?

Вот пример:
public static Iterable<Task> incoming(Iterable<Task>tasks, Date start, Date end)
    {
        Iterable<Task> result = null;
        if (tasks instanceof LinkedTaskList)
            result = new LinkedTaskList().getEmptyList();
        else
            result = new ArrayTaskList().getEmptyList();


        for (Task currentTask: tasks) {
            Date dateOfStart = currentTask.nextTimeAfter(start);
            if ( dateOfStart !=null &&
                    (dateOfStart.before(end) || dateOfStart.equals(end)) )
                result.add(currentTask);  // Error - add not works!
        }
        return result;
    }


Он не работает (
  • ,

Возвращайте массив нулевой длины, а не null

Нередко встречаются методы, имеющие следующий вид:
private List cheesesInStock = … ;
/**
* @return массив, содержащий все сыры, имеющиеся в магазине,
* или null, если сыров для продажи нет.
*/
public Cheese[] getCheeses() {
if (cheesesInStock.size() == 0)
return null;
}

Нет причин рассматривать как особый случай ситуацию, когда в продаже нет сыра. Это требует от клиента написания дополнительного кода для обработки возвращаемого методом значения пull, например:
Cheese[] cheeses = shop.getCheeses();
if (cheeses ! = пull &&
Аrrауs.аsList(shор.gеtСhееsеs()).соntаins(Сhееsе.SТILТON))
 Sуstеm.оut.рrintln("Jоllу good, just the thing.");

вместо простого:
if  (Аrrаys.аsList(shор.gеtСhееsеs()).соntаins(Сhееsе.SТILTON)) Sуstеm.оut.рrintln("Jоllу good, just the thing.");

Такого рода многоречивость необходима почти при каждом вызове метода, который вместо массива нулевой длины возвращает null. Это чревато ошибками, так как разработчик клиента мог и не написать специальный код для обработки результата null. Ошибка может оставаться незамеченной годами, поскольку подобные методы, как правило, возвращают один или несколько объектов. Следует еще упомянуть о том, что возврат null вместо массива приводит к усложнению самого метода, возвращающего массив.
Иногда можно услышать возражения, что возврат значения null предпочтительнее возврата массива нулевой длины потому, что это позволяет избежать расходов на размещение массива в памяти. Этот аргумент несостоятелен по двум причинам. Во-первых, на этом уровне нет смысла беспокоиться о производительности, если только профилирование программы не покажет, что именно этот метод является основной причиной падения производительности.
Во-вторых, при каждом вызове метода, который не возвращает записей, клиенту можно передавать один и тот же массив нулевой длины, поскольку любой массив нулевой длины неизменен, а неизменяемые объекты доступны для совместного использования. На самом деле, именно это и происходит, когда вы применяете стандартную идиому для выгрузки элементов из коллекции в массив с контролем типа:
private List сhееsеsInStосk = … ,
private fiпаl static Cheese[] NULL_CHEESE_ARRAY = nеw Cheese[0];
 /**
* @геtuгп массие, содержащий все сыры, имеющиеся в магазине
*/
public Cheese[] getCheeses() {
геtuгп (Cheese[] сhееsеsInStосk.tоАггау(NULL_СНЕЕSЕ_АRRАУ);  }

В этой идиоме константа в виде массива нулевой длины передается методу toArray для того, чтобы показать, какой тип он должен возвратить. Обычно метод toArray выделяет место в памяти для возвращаемого массива, однако если коллекция пуста,
она размещается во входном массиве, а спецификация Сollесtion,tоАггау(ОЬjесt[] дает гарантию, что если входной’ массив будет достаточно вместителен, чтобы содержать коллекцию, возвращен будет именно он. Поэтому представленная идиома никогда не будет сама размещать в памяти массив нулевой длины, а в качестве такового использует «константу с указанием типа».
Подведем итоги. Нет никаких причин для того, чтобы работающий с массивами метод возвращал значение null, а не массив нулевой длины. Такая идиома, по-видимому, проистекает из языка программирования С, где длина массива возвращается отдельно от самого массива. В языке С бесполезно выделять память под массив нулевой длины.
Источник: Джошуа Блох, Java TM Эффективное программирование, Издательство «Лори»
  • ,

Формирование множеств Set и Map

Привет друзья!
По какому принципу формируются коллекции Set и Map?

Они не упорядочены, но заметил (из заданий), что при создании set:

        set.add("арбуз");
        set.add("банан");
        set.add("вишня"); 
        <etc>

на выходе получается:
вишня
арбуз
земляника
<и т.д.>

при формировании map:

        map.put("арбуз", "ягода");
        map.put("банан", "трава");
        map.put("вишня", "ягода");


на выходе получается:
вишня — ягода
арбуз — ягода
земляника — ягода
<и т.д.>

т.е. Получается какое-то своеобразное упорядочивание.
  • ,

Часто задаваемые на собеседованиях вопросы по классам коллекциям в Java (Часть 1).

Без сомнения, коллекции в Java это крайне важная область, и вопросы по коллекциям будут задавать на собеседованиях как новичкам так и опытным программистам. Тема настолько обширна, что практически невозможно покрыть ее целиком. И все же, основываясь на моих предыдущих собеседованиях, я попробую перечислить как можно больше ХОРОШИХ вопросов, к которым вы должны быть готовы.
Вопросы будут как сложные так и простые, так что если вопрос кажется вам слишком примитивным — не забывайте что он отлично подойдет менее опытному программисту.
  • ,

Часто задаваемые на собеседованиях вопросы по классам коллекциям в Java (Часть 2).

  • ,

Как использовать классы - коллекции ArrayList, Vector и HashMap предоставляемые Java Collections Framework

В этой статье, мы узнаем о трех важных классах — коллекций ArrayList, Vector и HashMap из Collections Framework и начнем использовать их в собственном коде.

Используя классы — коллекции ArrayList и Vector, мы можем хранить группу элементов, в виде простых объектов и манипулировать ими, посредством различных методов, доступных в этих классах. Классы ArrayList и Vector доступны из пакете java.util.
Другой класс — коллекция доступный из пакета java.util, это HashMap, который позволяет хранить коллекцию отображений: ключ – значение. Это дает возможность получать нужное значение из коллекции, когда известен ключ. Давайте рассмотрим примеры с использованием этих классов – коллекций по очереди.