Java habrahabr – Java – General-purpose computer-programming language that is concurrent, class-based, object-oriented, and specifically designed to have as few implementation dependencies as possible / Habr

Содержание

Опасности обучения на Java / Habr

«Вы счастливчики. Мы по три месяца жили в мешках из дерюги в грязных сараях. Мы вставали в шесть утра, стирали мешки, съедали по корке чёрствого хлеба и шли работать на мельницу, по 14 часов в день, с понедельника и до воскресенья, и когда мы возвращались домой, наш папа порол нас своим ремнем»
— Летающий цирк Монти Пайтона, Четыре йоркширца

Ленивая молодёжь.

Что может быть хорошего в тяжёлой работе?

Верный признак моего старения — моё ворчание и жалобы о «современной молодёжи» и о том, как не хотят или не могут больше делать ничего сложного.

Когда я был молод, я учился программировать на перфокартах. Если вы случайно делали ошибку, у вас не было такой «современной возможности», как нажать клавишу backspace и ввести заново то, что нужно. Вам приходилось выкидывать карту и начинать ввод заново.

Когда я начинал проводить интервью с программистами в 1991 г., я обычно позволял им использовать любой язык программирования для решения моей задачи на кодирование. В 99% случаев они выбирали C.

В наши дни они обычно выбирают Java.

Не поймите меня неправильно: нет ничего неправильного в использовании Java в качестве рабочего языка.

Подождите минутку, я хочу немного изменить это утверждение. Я не утверждаю в этой отдельно взятой статье, что в использовании Java в качестве рабочего языка есть что-то неправильное. В этом много неправильного, но оно подождет до другой статьи.

Вместо этого я хочу сказать, что Java в целом недостаточно сложна, чтобы отделить отличных программистов от посредственных. Может быть, это отличный язык для работы, но сегодня речь не об этом. Я даже могу зайти так далеко, что скажу — факт того, что Java не сложен, это фича, а не баг, — но это ведёт к данной проблеме.

Это может звучать немного резковато, это просто моё скромное мнение, но есть две вещи, которым традиционно учат в университетах в курсе компьютерных наук (Computer Science, CS) и которые многие люди никогда полностью по-настоящему так и не понимают: указатели и рекурсия.

В самом начале обучения в колледже вы проходите курс структур данных, со связанными списками, хеш-таблицами и прочими мелочами, с широким использованием указателей. Такие курсы довольно часто используются как курсы для отсева: они так сложны, что все, кто не обладает мыслительными способностями, необходимыми для CS, бросают, и это очень хорошо, потому что если вы думаете, что указатели сложны, то подождите пока вам не придётся доказывать факты теории неподвижной точки.

Все те юные гении, которые в старших классах школы писали на Бейсике пинг-понг для Apple II, поступают в колледжи, выбирают CompSci 101, курс по структурам данных, и когда сталкиваются с работой с указателями, их мозги просто взрываются, и они решают перевестись на политологию, потому что теперь правовая школа кажется им лучшим выбором. Я много раз видел графики отсеивания студентов с курсов CS, и обычно процент выбывших составляет от 40% до 70%. В университетах склонны считать это разбазариванием; я думаю, что это просто необходимая естественная отбраковка людей, которые просто не смогут быть счастливы или успешны в карьере программиста.

Другим сложным для многих студентов был курс, в котором изучалось функциональное программирование, в том числе рекурсивное программирование. В Массачусетском Технологическом Институте была очень высоко поставлена планка по этим курсам, разработан обязательный курс (6.001) и учебник ( Абельсон и Сассман, Структуры и Интерпретация Компьютерных Программ (Abelson & Sussman’s Structure and Interpretation of Computer Programs)) которые используются десятками или даже сотнями лучших школ CS как стандарт де факто для курсов введения в CS.

Сложность этих курсов просто ошеломляющая. На первой лекции вы целиком изучаете Scheme, и теперь вы можете быть посвящены в работу функций, работающих с неподвижной точкой, которые используют другие функции на входе. Когда я смог побороть такой курс, CSE121 в Университете Пенсильвании, я увидел, как много, если не большинство, студентов так и не смогли это сделать. Материал был слишком сложным. Я послал по электронной почте профессору длинное письмо, полное рыданий, говорящее о том, что Это Просто Нечестно. Кто-то в университете, должно быть, услышал меня (или одного из других жалобщиков), потому что теперь на этом курсе изучают Java.

Теперь я бы предпочёл, чтобы меня не услышали.
Думаете, что вы знаете, что это такое? Проверьте Себя Здесь!

Об этом и ведутся споры. Годы скуления ленивых студентов, подобных мне, в сочетании с жалобами индустрии программного обеспечения на то, как мало американские университеты выпускают специалистов по CS, сделали своё дело, и за последнее десятилетие множество безупречных в остальном школ на 100% перешли на Java. И это приветствовалось: рекрутёрам, которые используют «grep» для оценки резюме (прим.: grep — это программа в Unix, позволяющая выбирать строки, в которых есть нужное слово

), это, кажется, нравится, и, что лучше всего, в Java нет ничего достаточно сложного, чтобы реально отсеять программистов без той части мозга, которая отвечает за указатели или рекурсии, соответственно уровень отсеивания в университетах понижается, факультеты CS выпускают больше студентов, за это получают больше денег, и всем становится хорошо.

Счастливые ученики Java-школ никогда не столкнутся с ужасными segfault (прим.: Segmentation Fault — типичная ошибка при обращении по некорректному адресу) при попытках реализовать основанные на указателях хеш-таблицы. Они никогда не будут делать безумных, сумасшедших попыток упаковать что-то в биты. Они никогда не будут загружать свои головы мыслями о том, как в полностью функциональных программах значение переменной никогда не изменяется, и все-таки оно постоянно изменяется! Парадокс!

Им не нужна эта часть мозгов, чтобы получить красный диплом.

Неужели я всего лишь один из тех старомодных ворчунов, вроде Четырёх Йоркширцев, которые хвастаются тем, как тяжело было жить в суровые старые времена?

Эй, в 1900 г. латынь и греческий были обязательными предметами в колледже, не потому, что они были как-то необходимы в жизни, но потому, что их знание было одним из обязательных признаков образованного человека. В некотором смысле мои аргументы не отличаются от тех аргументов, которые приводили сторонники латыни (все четыре). «[Латынь] тренирует ваш ум. Тренирует вашу память. Распутывание предложений на латыни — это отличное упражнение для ума, настоящая интеллектуальная головоломка, и хорошее введение в логическое мышление», писал Скотт Баркер (Scott Barker). Но я не смог найти ни одного университета, который до сих пор преподаёт латынь в обязательном порядке. Неужели указатели и рекурсия — это латынь и греческий компьютерных наук?

Итак, я легко соглашусь с тем, что программирование указателями сегодня не является необходимым в 90% разработки кода, и даже представляет опасность в промышленном коде. Да. Прекрасно. И функциональное программирование не так уж часто используется на практике. Согласен.

Но это все ещё важно для некоторых из самых восхитительных программных разработок. Например, без указателей вы никогда не сможете работать над ядром Linux. Вы не сможете понять ни строки кода Linux или любой операционной системы без реального понимания указателей.

Без понимания функционального программирования вы не сможете придумать MapReduce — алгоритма, который делает Google столь хорошо масштабируемым. Термины Map и Reduce пришли из Lisp и функционального программирования. MapReduce понятен любому, кто помнит из своего курса, эквивалентного 6.001, что истинно функциональные программы не имеют побочных эффектов и поэтому легко распараллеливаемы. Очень показателен тот факт, что в Google изобрели MapReduce, а в Microsoft нет, и это говорит кое-что о том, почему Microsoft до сих пор играет в догонялки, пытаясь заставить работать основные функции поисковой машины, в то время как в Google перешли к следующей проблеме:

построению Skynet величайшего в мире параллельного суперкомпьютера. Я не думаю, что Microsoft действительно понимает, насколько они отстали на этом пути.

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

Ничто в образовании, построенном полностью на Java, не отсеивает студентов из-за недостаточной гибкости их мозгов для понимания этих концепций. Как работодатель, я вижу, что 100%-Java школы начали штамповать выпускников курсов CS, некоторые из которых просто недостаточно умны для того, чтобы работать программистами с чем-то более замысловатым, чем Ещё Одно Бухгалтерское Приложение На Java, хотя они и управились со скрипом с «современной-упрощённой-для-тупиц» курсовой работой. Эти студенты никогда не справились бы с курсом 6.001 в Массачусетском Технологическом, или с CS 323 в Йеле, и, честно говоря, это и есть причина, почему, с точки зрения работодателя, диплом Массачусетского Технологического или Йеля имеет больший вес, чем диплом Дюка, который недавно стал Полностью-На-Java, или Penn University, который заменил Scheme и ML на Java, пытаясь преподавать на ней курс, который когда-то почти убил меня и моих друзей, CSE121. Дело не в том, что я не хочу нанимать умных ребят из Duke или Penn — я нанимаю их — просто мне гораздо сложнее понять, кто они. Раньше я мог сказать, что парень действительно умён, если он за несколько секунд может разобраться в рекурсивном алгоритме, или реализует функции, манипулирующие связными списками на основе указателей, с такой скоростью, с которой он может писать на листе бумаги. Но в случае выпускников школ Java я не могу сказать, имеет ли претендент проблемы с этими задачами, потому что у него просто не хватает соответствующего образования, или потому, что у него нет соответствующей части мозгов, необходимой для отличной работы в качестве программиста. Пол Грэхэм называет их Горе-Программистами.

Достаточно плохо уже то, что Школы-на-Java не отсеивают тех, кто никогда не сможет стать великими программистами, но на это школы вполне обоснованно могут сказать, что это не их проблема. Индустрия, или, по крайней мере, «рекрутёры-использующие-grep», действительно требуют, чтобы школы преподавали Java.

Но «школы-только-на-Java» также перестали тренировать мозги студентов для того, чтобы они стали знающими, проворными, и гибкими достаточно, чтобы могли хорошо проектировать программы (и я не имею в виду объектно-ориентированное «проектирование», когда вы тратите несчётное количество часов, переписывая собственный код при перестройке вашей объектной иерархии, или мучаетесь такими фальшивыми «проблемами», как выбор «содержит» или «является» (прим.:

has-a vs. is-a, выбор между наследованием и композицией классов)). Необходимы тренировки, чтобы научиться думать на нескольких уровнях абстракции одновременно, что является абсолютно необходимым для проектирования отличной архитектуры программного обеспечения.

Вам может быть интересно, может ли обучение объектно-ориентированному программированию (ООП) быть хорошим заменителем указателей и рекурсии как вычищающих курсов. Короткий ответ — нет. Не обсуждая достоинства ООП, можно просто сказать, что оно недостаточно сложно для отсеивания посредственных программистов. Преподавание ООП состоит в основном в запоминании нескольких словарных терминов, таких как «инкапсуляция» и «наследование» и заучивании ответов на множество вопросов о разнице между полиморфизмом и перегрузкой операций. Не сложнее заучивания знаменательных дат и имён на уроках истории, ООП ставит неадекватно простую задачу для того, чтобы отпугнуть первокурсников. Когда у вас проблемы с ООП, ваша программа все-таки работает, она просто становится сложной для поддержки. Якобы. Но когда у вас проблемы с указателями, ваша программа выдаёт Segmentation Fault, и вы понятия не имеете, что происходит, до тех пор пока вы не остановитесь, не сделаете глубокий вдох и не попытаетесь на самом деле заставить свой ум работать на двух уровнях абстракции одновременно.

Рекрутёры-использующие-grep, кстати, высмеиваются здесь, и для этого есть веские доводы. Я никогда не был знаком с человеком, который бы понимал Scheme, Haskell и указатели С, и не мог бы освоить Java за пару дней, и после этого писать код на Java лучше, чем люди, имеющие пятилетний опыт работы с Java, но попробуйте объяснить это среднему HR-биороботу.

А что насчёт миссии CS-факультетов? Это не ПТУ! Их работа не состоит в том, чтобы подготовить людей к работе в промышленности. Это только для общинных колледжей, скажут вам, (прим.: в США двухгодичный колледж, готовящий специалистов средней квалификации для работы на территории местного сообщества) и государственных программ переобучения для уволенных рабочих. Предполагается, что они дают студентам фундаментальные инструменты для того, чтобы те жили своей жизни, а не готовят их к первым дням работы. Правильно?

Всё же. CS это доказательства (рекурсия), алгоритмы (рекурсия), языки (лямбда-исчисление), операционные системы (указатели), компиляторы (лямбда-исчисление) — и в результате получаем, что школы-на-Java, которые не учат С и не учат Scheme, в действительности не учат компьютерной науке. Насколько бесполезно в реальном мире понимание концепции каррирования (прим.: в функциональном программировании порождения из одной функции другой функции с меньшим числом аргументов; например из f(x,y) = x*y получение функции f3(x) = f(x,3) = 3*x), настолько же это необходимая предпосылка для высшего образования в области CS. Я не понимаю, почему профессора в комитетах, утверждающих программы обучения в колледжах, позволяют их программам тупеть до такой степени, что они не только не могут готовить программистов, готовых к работе, они даже не могут подготовить выпускников, которые могли бы получить степень PhD (прим.: аналог кандидата наук) и соперничать с ними за рабочие места. Хотя нет, подождите. Забудьте. Может быть я всё-таки понимаю.

Действительно, если вы вернётесь и изучите дискуссии, которые имели место в академических кругах во время Великого Перехода На Java, вы заметите, какой большой интерес вызывал вопрос, явяется ли Java достаточно простым для использования его в качестве учебного языка.

«Боже мой», — подумал я, — «они пытались отупить курс ещё сильнее!» Почему бы не кормить всех студентов с ложечки? Почему бы преподавателям самим не делать и тесты за них — тогда точно никто не будет переходить на гуманитарные специальности! Как предполагается научить кого-то чему-то, если курс обучения заботливо сконструирован так, чтобы стать ещё проще, чем он есть сейчас? Кажется, ставится задача приложить усилия (PDF) по разработке простого подмножества Java, предназначенного для изучения студентами, выпуск упрощённой документации, которая заботливо прячет весь этот EJB/J2EE-мусор от их нежных мозгов так, чтобы они не беспокоили свои крохотные головки какими-то другими классами кроме тех, которые необходимы для того, чтобы решать стандартный простейший набор заданий по курсу.

Наиболее сочувственное объяснение, почему факультеты CS с таким энтузиазмом отупляют свои курсы, это то, что у них будет значительно больше времени для обучения актуальным концепциям CS, если только они не будут вынуждены тратить две лекции на то, чтобы объяснить разницу между, скажем, int и Integer в Java. Хорошо, если дело только в этом, 6.001 будет для вас отличным ответом: учебный язык Scheme так прост, что весь язык может быть рассказан смышлёным студентам примерно за десять минут; после этого вы можете потратить остаток семестра на неподвижные точки.

Фух.

Я возвращаюсь обратно к единицам и нулям.

(У вас есть единицы? Везучий ублюдок! Всё, что было у нас — это нули.)

Конец
Переводчик: Илья Болодурин

Cтатья давнишняя, но «зацепило».

Upd
В комментариях многие просили озвучить собственное мнение, а не тупой копипаст статьи. Ответил

Книжная полка: Java / Habr

По мотивам «JAVA → Java по-русски. Часть первая: Книги» habrahabr.ru/blogs/java/66568

Добрый день, уважаемые хабровчане!
В предыдущем обзоре Java книг, проведенном пользователем Scala, было рассказано о «практических» книгах, вышедших на рынок в последние три года. Я же хочу рассказать об этих и других книгах с несколько иного ракурса: попытаться выделить ценность информации книг.
Я не любитель читать с монитора, и предпочитаю книги покупать в бумажном варианте. Чтобы сделать осознанный выбор, следует отдавать себе отчет в том, что книга несет некие фундаментальные знания, но никак не сборник «последние фишки». Также в данной статье я хочу упомянуть о других известнейших трудах, не касающихся Java напрямую. Посмотрим, что лучше всего выбрать для обучения языку?

Итак, Java. Замечательный, на мой взгляд, язык программирования. Простой для изучения, имеющий широкие возможности и множество других полезных вещей. И начать изучение стоит с известной книги

К.Хорстманн – Java2: основы
Очень хорошая книга, показывающая основные возможности языка и самой платформы. Содержит описание основного API. На мой взгляд, она обязательна для приобретения, и не стоит даже заглядываться на толстый справочник Г.Шилдта.

«Выучив язык», надо уметь его правильно применять. В этом нам поможет

Г.Буч с его книгой «Объектно-ориентированный анализ и проектирование с примерами приложений»
Эта книга будет полезна как начинающим, так и продвинутым программистам. Основной её смысл – научить думать «объектно».
>прим: книга не относится напрямую к Java, поэтому отдельно ее не выделяю.

Само понимание объектно-ориентированного подхода дает превосходную почву для понимания паттернов проектирования. И в этом нам поможет

«Применение шаблонов Java» — С.Стелинг, О.Массен, 2001

«Почему паттерны, мы же и без них отлично кодим?» — довольно частый вопрос людей, «программирующих» на любом из ООП языков. Хочу эту ситуацию несколько прояснить: современное программирование уже не ремесло, а скорее фабрика, и важен не только результат, а скорость разработки, расширяемость и прочее. Это все не достижимо без грамотного проектирования. Во-вторых, чтение подобной литературы даст понимание самой организации Java, так как практически все в этом языке строится на паттернах.

Теперь можно браться за более крупные проекты, чем наши первые, получать новые знания. И поможет нам второй том книги

К.Хорстманн – Java2: тонкости программирования
Замечательная книга, показывающая неочевидные «штучки» очевидного процесса. Без опыта – не суйтесь в дебри.

Не могу не упомянуть мою любимую книгу от

Стива Макконнелла – Совершенный код
Раз уж вы связались с программированием, извольте читать ее от корки до корки каждый год-два.

Итак, мы уже поняли, что такое Java, и десктопные приложения нас больше не вдохновляют как раньше, потому мы решили писать серверные приложения.
Переходим к J2EE. Сразу отмечу, что для меня спецификация J2EE это не только сервлеты, jsp, jsf и пара Фреймворков. Для более глубоких знаний требуется совершенно другая литература, которую я не обнаружил среди русскоязычной (могу лишь посоветовать книги в оригинале, такие как «EJB 3 in Action» и «EJB Design Patterns: Advanced Patterns, Processes, and Idioms»).

Как уже упоминалось в статье хабропользователя Scala, книг по J2EE очень мало, и рассказать можно лишь о трех:

Буди Курняван — «Программирование web-приложений на языке Java»

Замечательная книга для новичка. В ней неплохо расписаны азы создания j2EE приложений на основе сервлетов и JSP. Мастера скажут, что это уже не актуально, и даже про EJB ничего не сказано. Но эта книга рассчитана на людей, только начавших знакомство с J2EE и работу серверных приложений слабо представляющих. Предоставляет базовые навыки разработки для Интернет. Так что свою миссию данное издание выполняет на все сто. Изучение фреймворков и других технологий – это уже отдельная история. Отдельно стоит сказать об отвратительном качестве печати: рисунки нечитабельны.
К.Хорстманн – «JavaServer faces»

Отличная книга по каркасу JSF из разряда must_have. Показывает все плюсы разработки на JSF в отношении удобства и скорости разработки, принцип построения приложения на «бобах» (beans), и многие другие преимущества этой технологии.
«Образцы J2EE. Лучшие решения и стратегии проектирования»

Дипак Алур, Джон Крупи и Дэн Малкс написали прекрасную книгу о паттернах для J2EE. Обязательна для чтения всем, кто сколько ни будь серьезно интересуется web-программированием на основе J2EE.

Почему я упустил из виду «сборник рецептов»? Да, она полезна, но эта не та книга, которую стоит покупать в бумажном варианте.

J2ME
Здесь дела обстоят совсем плохо. Все что мне доводилось читать сводилось к банальному тутореалу «напиши игру пока читаешь». Крайне бестолковая литература.

Вывод:

Все хотят быть на гребне волны в технологиях. Вот только технологии эти, к сожалению, рождаются на западе. Авторы пишут книги, их переводят… Тратится драгоценное время, и к выходу книги добрая половина тем уже не так актуальна. Но, тем не менее, чтение технической литературы (в т.ч. в оригинале) очень полезно в плане создания у себя в голове фундамента, на котором гораздо проще будет выстроить понимание современных технологий, нежели без них. Ведь работа программиста – это не просто набирать код, это тяжелый труд, требующий творческого подхода.
Что еще можно добавить? Для пущего эффекта — читаем оригиналы, документацию и форумы. Новичкам — в первую очередь — книги.

P.S.
Существуют книги – рассуждения, такие как «Гибкая разработка приложений на Java с использованием Spring, Hibernate и Eclipse». По сути, в них нет ни спринга ни хибернейта. Просто рассуждения на тему как нужно программировать. Описание подобных изданий я приводить не стану, пусть каждый себе выбирает сам, что ему читать перед сном.

UPD: Описание книги «Архитектура корпоративных приложений» М. Фаулера от пользователя sse

Мое мнение по поводу книги.
Первое, что отличает книгу — авторский стиль изложения. Все предельно ясно, и напоминает конспект, выполненный радивым студентом — записаны основные положения, выделены моменты, есть необходимые иллюстрации. Книга затрагивает широкий список вопросов: разделение приложения на слои, организация бизнес-логики, возможные сценарии работы с базой данных, организация UI в веб приложении (в книге не рассматривается UI для десктопных приложений, но впоследствии автор добавил эту информацию на свой сайт). Что немаловажно, приведены примеры на Java, что есть lingua franca для современного разработчика.
Всякий раз, когда в голове начинает крутиться мысль «а вот эту таблицу обернуть в класс, и тут унаследоваться — ура, я придумал новый способ», стоит взять книгу Фаулера и прочесть — вероятно, что там это задокументировано, изложено, описан усовершенствованный способ и приведены подводные камни и способы их обхода.
Книга разделена на две части, и этим очень напоминает структуру книги «Design Patterns» by GoF. Первая часть содержит общие положения о построении системы, а так же примеры применения знания из этой книги в реальных приложениях. Вторая часть является каталогом-справочником паттернов, сгруппированных по слоям приложения, в которых типично встретить каждый из них.
В завершение приведен ряд элементарных паттернов как наиболее общих и универсальных из области обработки и хранения данных, построения своих бизнес-объектов и советы по интерфейсной части.
Книга не из дешевых, но информация, приведенная в ней, себя окупает. На сайте Фаулера martinfowler.com она доступна в электронном варианте: можно «невозбранно» (с) ознакомиться, чтобы решить, нужна она вам, или нет. Я сразу понял, что нужна 🙂

UPD2: Описание от пользователя culvert изданий на английском языке

Список лучших, по моему мнению, когда либо написанных книг про Java. Книги расположены в порядке их важности (первыми указаны наиболее фундаментальные).

0. Java Language Specification 3rd Edition by James Gosling et al это самая главная книга, книга номер «ноль» этого списка, известная также как JLS. В третьей редакции описывается 5-ая версия языка. Я считаю, что каждый Java программист должен прочитать JLS хотя бы раз. Необходимо знать все возможности предоставленные языком для того чтобы использовать их самому и для того чтобы понимать код написанный другими. JLS это аналог JavaDoc или другой документации для используемых вами библиотек. Однако, в отличие от библиотечных классов, для которых документация обычно доступна из вашей среды программирования, по языку контекстная документация в средах программирования не предоставляется, поэтому JLS нужно читать практически «от корки до корки» хотябы на уровне оглавления и общего содержания каждого пункта. JLS является обязательной литературой при подготовке к сдаче экзамена на Sun Certified Java Programmer (SCJP).

1. Effective Java 2nd Edition by Joshua Bloch рассказывает как именно надо использовать язык Java, чтобы писать хорошие и понятные другим программы. Это сборник практических советов и шаблонов которые должны быть в репертуаре каждого Java программиста. Во второй редакции своего бестселлера Блох рассказывает об эффективном использовании всех возможностей 5-ой версии Java. Даже программист с многолетним опытом откроет в ней что-то новое и уж точно получит удовольствие от систематизации всех основных знаний про программирование на Java.

2. Java Concurrency in Practice by Brian Goetz et al(Must read если хотите писать хороший многопоточный код) необходима всем, кто пишет многопоточные приложения на Java. Её не нужно читать если вы пишете простые приложения а для бизнес-логики используете J2EE или другие каркасы приложений которые изолируют программиста от многопоточности. Однако, как только вам необходимо оптимизировать производительность или скорость реакции вашего приложения, то в современном мире от многопоточности вам не уйти. В этом случае книга Гоэтза будет вашим лучшим помощником.

3. Java Puzzlers: Traps, Pitfalls, and Corner Cases by Joshua Bloch, Neal Gafter это интересная книга которая состоит из интеллектуальных загадок исключительно на языке Java. Она раскроет перед вами все граничные случаи языка Java, научит как не надо программировать, чтобы ваш код был понятен. Книжка не обязательна для прочтения, но очень рекомендуется для подготовки к SCJP и для собственного удовольствия.

Справочник по Java Collections Framework / Habr

Данная публикация не является полным разбором или анализом (не покрывает пакет java.util.concurrent). Это, скорее, справочник, который поможет начинающим разработчикам понять ключевые отличия одних коллекций от других, а более опытным разработчикам просто освежить материал в памяти.
Что такое Java Collections Framework?

Java Collection Framework — иерархия интерфейсов и их реализаций, которая является частью JDK и позволяет разработчику пользоваться большим количесвом структур данных из «коробки».
Базовые понятия

На вершине иерархии в Java Collection Framework располагаются 2 интерфейса: Collection и Map. Эти интерфейсы разделяют все коллекции, входящие во фреймворк на две части по типу хранения данных: простые последовательные наборы элементов и наборы пар «ключ — значение» (словари).



Collection — этот интерфейс находится в составе JDK c версии 1.2 и определяет основные методы работы с простыми наборами элементов, которые будут общими для всех его реализаций (например size(), isEmpty(), add(E e) и др.). Интерфейс был слегка доработан с приходом дженериков в Java 1.5. Так же в версии Java 8 было добавлено несколько новых метода для работы с лямбдами (такие как stream(), parallelStream(), removeIf(Predicate<? super E> filter) и др.).

Важно также отметить, что эти медоды были реализованы непосредственно в интерфейсе как default-медоды.

Map. Данный интерфейс также находится в составе JDK c версии 1.2 и предоставляет разработчику базовые методы для работы с данными вида «ключ — значение».Также как и Collection, он был дополнен дженериками в версии Java 1.5 и в версии Java 8 появились дополнительные методы для работы с лямбдами, а также методы, которые зачастую реализовались в логике приложения (getOrDefault(Object key, V defaultValue), putIfAbsent(K key, V value)).

Интерфейс Map [doc]

Hashtable — реализация такой структуры данных, как хэш-таблица. Она не позволяет использовать null в качестве значения или ключа. Эта коллекция была реализована раньше, чем Java Collection Framework, но в последствии была включена в его состав. Как и другие коллекции из Java 1.0, Hashtable является синхронизированной (почти все методы помечены как synchronized). Из-за этой особенности у неё имеются существенные проблемы с производительностью и, начиная с Java 1.2, в большинстве случаев рекомендуется использовать другие реализации интерфейса Map ввиду отсутствия у них синхронизации.

HashMap — коллекция является альтернативой Hashtable. Двумя основными отличиями от Hashtable являются то, что HashMap не синхронизирована и HashMap позволяет использовать null как в качестве ключа, так и значения. Так же как и Hashtable, данная коллекция не является упорядоченной: порядок хранения элементов зависит от хэш-функции. Добавление элемента выполняется за константное время O(1), но время удаления, получения зависит от распределения хэш-функции. В идеале является константным, но может быть и линейным O(n). Более подробную информацию о HashMap можно почитать здесь (актуально для Java < 8).

LinkedHashMap — это упорядоченная реализация хэш-таблицы. Здесь, в отличии от HashMap, порядок итерирования равен порядку добавления элементов. Данная особенность достигается благодаря двунаправленным связям между элементами (аналогично LinkedList). Но это преимущество имеет также и недостаток — увеличение памяти, которое занимет коллекция. Более подробная информация изложена в этой статье.

TreeMap — реализация Map основанная на красно-чёрных деревьях. Как и LinkedHashMap является упорядоченной. По-умолчанию, коллекция сортируется по ключам с использованием принципа «natural ordering», но это поведение может быть настроено под конкретную задачу при помощи объекта Comparator, которые указывается в качестве параметра при создании объекта TreeMap.

WeakHashMap — реализация хэш-таблицы, которая организована с использованием weak references. Другими словами, Garbage Collector автоматически удалит элемент из коллекции при следующей сборке мусора, если на ключ этого элеметна нет жёстких ссылок.

Интерфейс List [doc]

Реализации этого интерфейса представляют собой упорядоченные коллекции. Кроме того, разработчику предоставляется возможность доступа к элементам коллекции по индексу и по значению (так как реализации позволяют хранить дубликаты, результатом поиска по значению будет первое найденное вхождение).

Vector — реализация динамического массива объектов. Позволяет хранить любые данные, включая null в качестве элемента. Vector появился в JDK версии Java 1.0, но как и Hashtable, эту коллекцию не рекомендуется использовать, если не требуется достижения потокобезопасности. Потому как в Vector, в отличии от других реализаций List, все операции с данными являются синхронизированными. В качестве альтернативы часто применяется аналог — ArrayList.

Stack — данная коллекция является расширением коллекции Vector. Была добавлена в Java 1.0 как реализация стека LIFO (last-in-first-out). Является частично синхронизированной коллекцией (кроме метода добавления push()). После добавления в Java 1.6 интерфейса Deque, рекомендуется использовать именно реализации этого интерфейса, например ArrayDeque.

ArrayList — как и Vector является реализацией динамического массива объектов. Позволяет хранить любые данные, включая null в качестве элемента. Как можно догадаться из названия, его реализация основана на обычном массиве. Данную реализацию следует применять, если в процессе работы с коллекцией предплагается частое обращение к элементам по индексу. Из-за особенностей реализации поиндексное обращение к элементам выполняется за константное время O(1). Но данную коллекцию рекомендуется избегать, если требуется частое удаление/добавление элементов в середину коллекции. Подробный анализ и описание можно почитать в этом хабратопике.

LinkedList — ещё одина реализация List. Позволяет хранить любые данные, включая null. Особенностью реализации данной коллекции является то, что в её основе лежит двунаправленный связный список (каждый элемент имеет ссылку на предыдущий и следующий). Благодаря этому, добавление и удаление из середины, доступ по индексу, значению происходит за линейное время O(n), а из начала и конца за константное O(1). Так же, ввиду реализации, данную коллекцию можно использовать как стек или очередь. Для этого в ней реализованы соответсвующие методы. На Хабре также есть статья с подробным анализом и описанием этой коллекции.

Интерфейс Set [doc]

Представляет собой неупорядоченную коллекцию, которая не может содержать дублирующиеся данные. Является программной моделью математического понятия «множество».

HashSet — реализация интерфейса Set, базирующаяся на HashMap. Внутри использует объект HashMap для хранения данных. В качестве ключа используется добавляемый элемент, а в качестве значения — объект-пустышка (new Object()). Из-за особенностей реализации порядок элементов не гарантируется при добавлении.

LinkedHashSet — отличается от HashSet только тем, что в основе лежит LinkedHashMap вместо HashSet. Благодаря этому отличию порядок элементов при обходе коллекции является идентичным порядку добавления элементов.

TreeSet — аналогично другим классам-реализациям интерфейса Set содержит в себе объект NavigableMap, что и обуславливает его поведение. Предоставляет возможность управлять порядком элементов в коллекции при помощи объекта Comparator, либо сохраняет элементы с использованием «natural ordering».

Интерфейс Queue [doc]

Этот интерфейс описывает коллекции с предопределённым способом вставки и извлечения элементов, а именно — очереди FIFO (first-in-first-out). Помимо методов, определённых в интерфейсе Collection, определяет дополнительные методы для извлечения и добавления элементов в очередь. Большинство реализаций данного интерфейса находится в пакете java.util.concurrent и подробно рассматриваются в данном обзоре.

PriorityQueue — является единственной прямой реализацией интерфейса Queue (была добавлена, как и интерфейс Queue, в Java 1.5), не считая класса LinkedList, который так же реализует этот интерфейс, но был реализован намного раньше. Особенностью данной очереди является возможность управления порядком элементов. По-умолчанию, элементы сортируются с использованием «natural ordering», но это поведение может быть переопределено при помощи объекта Comparator, который задаётся при создании очереди. Данная коллекция не поддерживает null в качестве элементов.

ArrayDeque — реализация интерфейса Deque, который расширяет интерфейс Queue методами, позволяющими реализовать конструкцию вида LIFO (last-in-first-out). Интерфейс Deque и реализация ArrayDeque были добавлены в Java 1.6. Эта коллекция представляет собой реализацию с использованием массивов, подобно ArrayList, но не позволяет обращаться к элементам по индексу и хранение null. Как заявлено в документации, коллекция работает быстрее чем Stack, если используется как LIFO коллекция, а также быстрее чем LinkedList, если используется как FIFO.

Заключение

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

При необходимости, разработчик может создать собственную реализацию, расширив или переопределив существующую логику, либо создав свою собственную реализацию подходящего интерфейса с нуля. Также существует некоторое количество готовых решений, которые являются альтернативой или дополнением к Java Collections Framework. Наиболее популярными являются Google Guava и Commons Collections.

В дополнение, хотелось бы указать в качестве дополнительного материала, ссылку на обзор пакета java.util.concurrent. Который является отличным дополнением к изложенному материалу.

Введение в RxJava: Почему Rx? / Habr

Этот цикл статей предназначен для знакомства начинающего реактивного программиста с мощью библиотеки RxJava ˜— реализации принципов реактивного программирования для JVM. Это перевод обширного туториала по RxJava Крисса Фруссиоса, основанного на IntroToRx для Rx.NET.

Для следования этой обучающей программе от вас не потребуются знания реактивного или функционального программирования, однако, предполагается наличие базовых знаний Java.

Материал этих статей расчитан на прочтение от начала до конца. Его обьем больше, чем среднего туториала, но меньше чем реальной книги. Мы начнем с самых основ и от раздела к разделу будем переходить к всё более продвинутым сценариям и концепциям. Каждый раздел задумывался самодостаточным и лаконичным для того, чтобы к нему можно было вернуться в будущем.

Примеры к этим материалам доступны в двух видах:


  1. Примеры с выводом в консоль (рекоммендовано для читающих в первый раз)
  2. Самопроверяющиеся примеры в форме JUnit тестов

Содержание:


Часть первая – Вступление: Почему Rx?

Пользователи ожидают данных в реальном времени. Они хотят твиты сейчас. Подтвержение заказа сейчас. Им необходимы цены по состоянию на сейчас. Как разработчик, вы нуждаетесь в самонаводящихся сообщениях. Вы не хотите быть блокированным в ожидании результата. Вы хотите, чтобы результат пришел к вам по готовности. Даже более того, вы хотите работать с результатом по частям: вы не хотите ждать пока загрузится всё перед тем как отобразить первую строку. Мир перешел в режим уведомлений. У разработчиков есть инструменты, чтобы уведомлять, это легко. Им нужны инструменты чтобы реагировать на уведомления.

Добро пожаловать в Rx. Rx – это мощный инструмент, который позволяет решать проблемы в элегантном декларативном стиле, присущем функциональному программированию. Rx обладает следующими преимуществами:


  • Интуитивность
    • Действия в Rx описываются в таком же стиле, как и в других библиотеках вдохновленных функциональным программированием, например, Java Streams. Rx дает возможность использовать функциональные трансформации над потоками событий.

  • Расширяемость
    • RxJava может быть расширена пользовательскими операторами. И хотя Java не позволяет сделать это элегантным образом, RxJava предлагает всю расширяемость доступную в реализациях Rx на любом другом языке.

  • Декларативность
    • Функциональные трансформации обьявлены декларативно.

  • Компонуемость
    • Операторы в Rx легко компонуются, чтобы проводить сложные операции.

  • Преобразуемость
    • Операторы в Rx могут трансформировать типы данных, фильтруя, обрабатывая и расширяя потоки данных при необходимости.


Когда следует использовать Rx?

Rx применяется для составления и обработки последовательностей событий.


Следует использовать Rx

  • UI события, такие как mouse move, button click
  • События вроде изменения свойства, обновления коллекции, «Заказ оформлен», «Регистрация закончена» и т.д.
  • Инфраструктурные события (сообщения от системы, WMI или файловых менеджеров)
  • Интеграция с событиями от шины сообщений (message bus), сообщениями из WebScoket API
  • Интеграция с CEP-системами (StreamInsight, StreamBas)

Возможно использование Rx

  • Результат Future или похожего паттерна

Не следует использовать Rx

  • Для превращения Iterable в Observable только для того, чтобы работать с ними через библиотеку Rx.

В следующей части мы рассмотрим фундаментальные типы, на которых основана концепция реактивного программирования: Observable и Observer.

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

Java собеседование. Коллекции / Habr

С недавнего времени у меня появилась настойчивая мысль, что профессиональное развитие сильно замедлилось и это хочется как-то исправить. Да, читаю книги, слушаю курсы, но в то же время приходит и понимание того, что возможно пришло время сменить работу, здесь вроде как все изучено, плавно уходим в рутину. Данная мысль сподвигла меня на рассылку своего резюме в несколько компаний — лидеров рынка. После прохождения собеседования в 3 из них, я решил, как водится внести свои 5 копеек в освещение обширной темы собеседования, а именно технических вопросов по Java коллекциям, с которыми приходится сталкиваться. Да, знаю, читатель скажет: «коллекции — избитая тема, сколько можно», но часть из приведенных ниже вопросов, я задавал своим знакомым разработчикам, которые занимают именно позиции разработчиков («крепких середнячков», по меркам недалекой от Москвы глубинки, которые уверенно справляются со своей работой на практике, а вот в теории скажем так есть пробелы, потому, что работа не требует решения каких-то нетривиальных задач, да и потому что не всем это интересно — изучать как внутри работает структура данных), вызывало растерянность. Думаю, что рассмотренный материал будет не очень интересен разработчикам выше уровня Junior (я попрошу их комментировать, дополнять и критиковать изложенный здесь материал), а вот Junior`ы уверен, найдут в этой статье интересное для себя.

Признаюсь честно, сам при прохождении интервью не знал ответы на некоторые из изложенных ниже вопросов, хотя вроде как уже прошел этап джуниорства. Это вдвойне обидно, с учетом того, что позиции в те компании, где симпатию вызывало все, начиная от общения с HR и заканчивая возможной будущей сферой деятельности не удалось получить оффер и как раз там были вопросы по коллекциям, с которыми я не справился (уверен они внесли свою негативную лепту). А вот там, где все прошло вполне неплохо с точки зрения собеседования, предложенная сфера деятельности и общение в целом с будущими коллегами оставили негатив, так что закон «подлости» во всей красе. В итоге, данным топиком я хочу и в своей голове заполнить обнаруженные пробелы+систематизировать на «бумаге» эти знания.

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

1. Чем отличается ArrayList от LinkedList?

В моем рейтинге это один из двух самых популярных вопросов о коллекции, задают в 90% случаев. Вызвал у меня проблему на моем первом собеседовании на Junior Developer`а. Вкратце ответ на этот вопрос сводится к следующему: ArrayList это список, реализованный на основе массива, а LinkedList — это классический связный список, основанный на объектах с ссылками между ними.

Преимущества ArrayList: в возможности доступа к произвольному элементу по индексу за постоянное время (так как это массив), минимум накладных расходов при хранении такого списка, вставка в конец списка в среднем производится так же за постоянное время. В среднем потому, что массив имеет определенный начальный размер n (в коде это параметр capacity), по умолчанию n = 10, при записи n+1 элемента, будет создан новый массив размером (n * 3) / 2 + 1, в него будут помещены все элементы из старого массива + новый, добавляемый элемент. В итоге получаем, что при добавлении элемента при необходимости расширения массива, время добавления будет значительно больше, нежели при записи элемента в готовую пустую ячейку. Тем не менее, в среднем время вставки элемента в конец списка является постоянным. Удаление последнего элемента происходит за константное время. Недостатки ArrayList проявляются при вставке/удалении элемента в середине списка — это взывает перезапись всех элементов размещенных «правее» в списке на одну позицию влево, кроме того, при удалении элементов размер массива не уменьшается, до явного вызова метода trimToSize().

LinkedList наоборот, за постоянное время может выполнять вставку/удаление элементов в списке (именно вставку и удаление, поиск позиции вставки и удаления сюда не входит). Доступ к произвольному элементу осуществляется за линейное время (но доступ к первому и последнему элементу списка всегда осуществляется за константное время — ссылки постоянно хранятся на первый и последний, так что добавление элемента в конец списка вовсе не значит, что придется перебирать весь список в поисках последнего элемента). В целом же, LinkedList в абсолютных величинах проигрывает ArrayList и по потребляемой памяти и по скорости выполнения операций. LinkedList предпочтительно применять, когда происходит активная работа (вставка/удаление) с серединой списка или в случаях, когда необходимо гарантированное время добавления элемента в список.

Для углубленного и в то же время экспресс обучения очень рекомендую к прочтению замечательные статьи tarzan82 о ArrayList и LinkedList. Так же порекомендую статью от lany о потреблении памяти коллекциями — очень познавательно.

2. Что вы обычно используете (ArrayList или LinkedList)? Почему?

Это вопрос является слегка замаскированной версией предыдущего, так как ответ на этот вопрос приведет к постепенному изложению ответа на предыдущей вопрос. В 90% случае ArrayList будет быстрее и экономичнее LinkedList, так что обычно используют ArrayList, но тем не менее всегда есть 10% случаев для LinkedList. Я говорю, что обычно ArrayList использую, ссылаясь на тесты и последний абзац из предыдущего вопроса, но не забываю и про LinkedList (в каких случаях? так же последний абзац предыдущего вопроса помогает).

3. Что быстрее работает ArrayList или LinkedList?

Еще одна замаскированная версия первого вопроса. Хитрее приведенных выше вариантов, что постановка вопроса подразумевает односложный ответ с выбором одного из предложенных вариантов, что, по задумке автора вопроса, как я понимаю, должно сразу выявить человека с неглубокими познаниями в collections. Правильным же действием будет встречный вопрос о том, какие действия будут выполняться над структурой. В итоге, диалог плавно переходит к ответу на первый вопрос.

4. Необходимо добавить 1млн. элемент, какую структуру вы используете?

Тоже довольно популярная скрытая версия первого вопроса. Так же постановка предполагает выбор одного из предложенных вариантов, хотя на самом деле информации для однозначного выбора нет. Нужно задавать дополнительные вопросы: в какую часть списка происходит добавление элементов? есть ли информация о том, что потом будет происходить с элементами списка? какие то ограничения по памяти или скорости выполнения? В целом, все тот же первый вопрос, но немного с другой стороны: вы через дополнительные вопросы, показываете глубину понимания работы Array и Linked List.
Однажды я сам «клюнул» на этот крючок, домыслив про себя, что добавить — это «вставить» в конец списка и усиленно продвигал ArrayList, хотя ничего не знал (и не пытался узнать) про дальнейшие действие с этим списком и возможные ограничения.

5. Как происходит удаление элементов из ArrayList? Как меняется в этом случае размер ArrayList?

Опять же, ответ на вопрос 1 содержит ответ и на этот вопрос. При удалении произвольного элемента из списка, все элементы находящиеся «правее» смещаются на одну ячейку влево и реальный размер массива (его емкость, capacity) не изменяется никак. Механизм автоматического «расширения» массива существует, а вот автоматического «сжатия» нет, можно только явно выполнить «сжатие» командой trimToSize().

6. Предложите эффективный алгоритм удаления нескольких рядом стоящих элементов из середины списка, реализуемого ArrayList.

Неизбитый, по моим меркам вопрос, встречался мне всего однажды, когда я не знал механизма удаления элементов из ArrayList. В итоге вызвал у меня серьезные затруднения. На самом деле все довольно просто и очевидно, когда знаешь как происходит удаление одного элемента. Допустим нужно удалить n элементов с позиции m в списке. Вместо выполнения удаления одного элемента n раз (каждый раз смещая на 1 позицию элементы, стоящие «правее» в списке), нужно выполнить смещение всех элементов, стоящих «правее» n+m позиции на n элементов левее к началу списка. Таким образом, вместо выполнения n итераций перемещения элементов списка, все выполняется за 1 проход.

7. Как устроена HashMap?

Это второй из списка самых популярных вопросов по коллекциям. Уж даже не помню был ли случай, когда этот вопрос мне не задавали.

Вкратце, HashMap состоит из «корзин» (bucket`ов). С технической точки зрения «корзины» — это элементы массива, которые хранят ссылки на списки элементов. При добавлении новой пары ключ-значение, вычисляет хеш-код ключа, на основании которого вычисляется номер корзины (номер ячейки массива), в которую попадет новый элемент. Если корзина пустая, то в нее сохраняется ссылка на вновь добавляемый элемент, если же там уже есть элемент, то происходит последовательный переход по ссылкам между элементами в цепочке, в поисках последнего элемента, от которого и ставится ссылка на вновь добавленный элемент. Если в списке был найден элемент с таким же ключом, то он заменяется. Добавление, поиск и удаление элементов выполняется за константное время. Вроде все здорово, с одной оговоркой, хеш-функций должна равномерно распределять элементы по корзинам, в этом случае временная сложность для этих 3 операций будет не ниже lg N, а в среднем случае как раз константное время.

В целом, этого ответа вполне хватит на поставленный вопрос, дальше скорее всего завяжется диалог по HashMap, с углубленным пониманием процессов и тонкостей.

Опять же, рекомендую к прочтению статью tarzan82 по HashMap.

8. Какое начальное количество корзин в HashMap?

Довольно неожиданный вопрос, опять же меня он когда-то заставил угадывать число корзин при использовании конструктора по умолчанию.

Ответ здесь — 16. Отвечая, стоит заметить, что можно используя конструкторы с параметрами: через параметр capacity задавать свое начальное количество корзин.

9. Какая оценка временной сложности выборки элемента из HashMap? Гарантирует ли HashMap указанную сложность выборки элемента?

Ответ на первую часть вопроса, можно найти в ответе на вопрос 7 — константное время необходимо для выборки элемента. Вот на второй части вопроса, я недавно растерялся. И устройство HashMap знал и про хеш-функцию тоже знал, а вот к такому вопросу не был готов, в уме кинулся вообще в другом направлении и сосредоточился на строении HashMap откинув проблему хеш-кода, который в голове всегда привык считать хеш-кодом с равномерным распределением. На самом деле ответ довольно простой и следует из ответа вопроса 7.

Если вы возьмете хеш-функцию, которая постоянно будет возвращать одно и то же значение, то HashMap превратится в связный список, с отвратной производительностью. Затем даже, если вы будете использовать хеш-функцию с равномерным распределением, в предельном случае гарантироваться будет только временная сложность lg N. Так что, ответ на вторую часть вопроса — нет, не гарантируется.

10. Роль equals и hashCode в HashMap?

Ответ на этот вопрос следует из ответа на вопрос 7, хотя явно там и не прописан. hashCode позволяет определить корзину для поиска элемента, а equals используется для сравнения ключей элементов в списке внутри корзины и искомого ключа.

11. Максимальное число значений hashCode()?

Здесь все довольно просто, достаточно вспомнить сигнатуру метода: int hashCode(). То есть число значений равно диапазону типа int — 2^32 (точного диапазона никогда не спрашивали, хватало такого ответа).

12. Как и когда происходит увеличение количества корзин в HashMap?

Вот это довольно тонкий вопрос. Как показал мой мини-опрос, если суть устройства HashMap себе представляют многие более-менее ясно, то этот вопрос часто ставил собеседника в тупик.

Помимо capacity в HashMap есть еще параметр loadFactor, на основании которого, вычисляется предельное количество занятых корзин (capacity*loadFactor). По умолчанию loadFactor = 0,75. По достижению предельного значения, число корзин увеличивается в 2 раза. Для всех хранимых элементов вычисляется новое «местоположение» с учетом нового числа корзин.

13. В каком случае может быть потерян элемент в HashMap?

Этот интересный вопрос мне прислал LeoCcoder, у меня подобного не спрашивали и честно признаюсь, после прочтения сходу не смог придумать сценарий для потери элемента. Все опять же оказалось довольно просто, хоть и не так явно: допустим в качестве ключа используется не примитив, а объект с несколькими полями. После добавления элемента в HashMap у объекта, который выступает в качестве ключа, изменяют одно поле, которое участвует в вычислении хеш-кода. В результате при попытке найти данный элемент по исходному ключу, будет происходить обращение к правильной корзине, а вот equals (ведь equals и hashCode должны работать с одним и тем же набором полей) уже не найдет указанный ключ в списке элементов. Тем не менее, даже если equals реализован таким образом, что изменение данного поля объекта не влияет на результат, то после увеличения размера корзин и пересчета хеш-кодов элементов, указанный элемент, с измененным значением поля, с большой долей вероятности попадет совсем в другую корзину и тогда он уже совсем потеряется.

14. Почему нельзя использовать byte[] в качестве ключа в HashMap?

Еще один вопрос от LeoCcoder. Как обычно, все оказалось довольно просто — хеш-код массива не зависит от хранимых в нем элементов, а присваивается при создании массива (метод вычисления хеш-кода массива не переопределен и вычисляется по стандартному Object.hashCode() на основании адреса массива). Так же у массивов не переопределен equals и выполняет сравнение указателей. Это приводит к тому, что обратиться к сохраненному с ключом-массивом элементу не получится при использовании другого массива такого же размера и с такими же элементами, доступ можно осуществить лишь в одном случае — при использовании той же самой ссылки на массив, что использовалась для сохранения элемента. За ответ на этот вопрос отдельная благодарность уходит пользователю @dark_dimius.

15. В чем отличия TreeSet и HashSet?

Начнем с того, что Set — это множество (так же называют «набором»). Set не допускает хранение двух одинаковых элементов. Формально говоря, термин «множество» и так обозначает совокупность различных элементов, очень важно, что именно различных элементов, так как это главное свойство Set. С учетом такого определения, пояснение про хранение одинаковых элементом не требуется, но в обиходе, понятие «множество» потеряло свой строгий смысл касательно уникальности элементов, входящих в него, поэтому все же уточняйте отдельно данное свойство множества.

TreeSet обеспечивает упорядоченно хранение элементов в виде красно-черного дерева. Сложность выполнения основных операций в TreeSet lg N. HashSet использует для хранения элементов такой же подход, что и HashMap, за тем отличием, что в HashSet в качестве ключа выступает сам элемент, кроме того HashSet (как и HashMap) не поддерживает упорядоченное хранение элементов и обеспечивает временную сложность выполнения операций аналогично HashMap.

16. Устройство TreeSet?

Этот вопрос задают вместо вопроса 14 и здесь достаточно краткого ответа, что TreeSet основан на красно-черном дереве. Как правило этого хватает и собеседник сразу переходит к следующему вопросу, у меня ни разу не спрашивали механизм балансировки дерева или другие подробности его реализации.

Для экспресс углубления знаний по красно-черному дереву рекомендую вот эту статью.

17. Что будет, если добавлять элементы в TreeSet по возрастанию?

Обычно данный вопрос собеседник предваряет фразой, что в основе TreeSet лежит бинарное дерево и если добавлять элементы по возрастанию, то как они будут распределены по дереву.

Если нет точного представления об устройстве TreeSet, а есть общее понимание о том, что это бинарное дерево (в чем нас дополнительно уверяет собеседник), то данный вопрос может привести к интересному результату: все элементы после доабвления в обычное бинарное дерево будут находится в одной ветви длиной N элементов, что сводит на нет, все преимущества такой структуры, как дерево (фактически получается список). На самом, деле, как выше упоминалось в основе TreeSet лежит красно-черное дерево, которое умеет само себя балансировать. В итоге, TreeSet все равно в каком порядке вы добавляете в него элементы, преимущества этой структуры данных будут сохраняться.

Заключение

Надеюсь, рассмотренные вопросы будут полезны хабраюзерам. Прошу так же простить мне возможную некоторую наивность в том, что приведенные выше вопросы требуют такого детального рассмотрения, но в свое время подобная статья мне бы серьезно помогла. Уверен, что в статье присутствуют неточности — прошу в комментарии, кроме того, надеюсь, что более опытные товарищи в комментариях будут активно делится вопросами из своей практики и, если статья будет благосклонно принята хабрасообществом, то вполне возможно продолжение обзора технических вопросов для Java собеседований.

P.S. Немного меркантильного интереса: поиски нового места работы продолжаются и если кто-то из хабраюзеров в процессе поиска Java разработчика в компанию с современным подходом к разработке и интересными задачами или просто может порекомендовать присмотреться к какой-либо подходящей вакансии — я буду благодарен, прошу в личку.

Как подружить Java и C++. Часть первая / Habr

Здравствуйте.

Как вы, наверное, уже догадались, речь пойдет о JNI. Для тех, кто не знает что это, объясняю: JNI (или java native interface) — это такая штука, которая позволяет делать вызовы нативного кода из java машины и наоборот.

Зачем это может потребоваться? Есть несколько причин: необходимость использовать код, который уже написан для нативной платформы, необходимость реализовать что-то такое, что невозможно сделать с помощью одной JVM (например, работа с какими-нибудь специфическими железками), ну и ускорение выполнения критических кусков кода (правда, это весьма спорный момент).

Допустим, у нас есть какой-то java класс из которого надо вызвать метод, написанный на c++ и находящийся в динамически связываемой библиотеке (например, в windows это будет dll). Что мы должны для этого сделать?

Для начала мы объявляем метод какого-нибудь класса как native. Это будет означать, что JVM при вызове этого метода будет передавать управление нативному коду.

Затем, нам надо загрузить нативную библиотеку. Для этого можно вызвать System.loadLibrary(String), которая принимает в качестве параметра имя библиотеки. После этого вызова библиотека будет загружена в адресное пространство JVM.

Теперь, представим, что у нас есть следующий java класс:

package my.mega.pack;
 
public class NativeCallsClass
{
    static
    {
       System.loadLibrary(«megalib»);
    }
 
    native public static void printOne();
    native public static void printTwo();
}
Здесь мы, для удобства, вынесли loadLibrary() в static область класса.

Допустим, теперь, что мы вызываем NativeCallsClass.printOne(). Тогда JVM будет искать в библиотеках метод со следующим именем: Java_my_mega_pack_NativeCallsClass_printOne(...).

Мы написали класс на java, у которого есть методы, помеченные как native. Теперь нам надо создать хедеры с объявлениями функций C++, которые мы хотим вызывать.

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

javac -d bin/ src/my/mega/pack/NativeCallsClass.java
cd bin
javah my.mega.pack.NativeCallsClass

Мы компилируем класс, а потом используем утилиту javah. После этого у нас появится файл, который называется my_mega_pack_NativeCallsClass.h. Это и есть наш хедер. Выглядит он примерно так:
/* DO NOT EDIT THIS FILE — it is machine generated */
#include <jni.h>
/* Header for class my_mega_pack_NativeCallsClass */
 
#ifndef _Included_my_mega_pack_NativeCallsClass
#define _Included_my_mega_pack_NativeCallsClass
#ifdef __cplusplus
extern «C» {
#endif
/*
 * Class:     my_mega_pack_NativeCallsClass
 * Method:    printOne
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printOne
  (JNIEnv *, jclass);
 
/*
 * Class:     my_mega_pack_NativeCallsClass
 * Method:    printTwo
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printTwo
  (JNIEnv *, jclass);
 
#ifdef __cplusplus
}
#endif
#endif
Самое главное здесь — это сигнатуры 2х функций: Java_my_mega_pack_NativeCallsClass_printOne(JNIEnv *env, jclass myclass) и Java_my_mega_pack_NativeCallsClass_printTwo(JNIEnv *env, jclass myclass).

Их-то нам и надо реализовать. Для начала разберемся с их сигнатурами. env — это интерфейс к виртуальной машине. Все операции с JVM выполняются с помощью него. Позже мы разберем это подробнее. myclass — это идентификатор java класса, у которого есть метод native, отождествленный с этой функцией, то есть в нашем случае это NativeCallsClass. Обратите внимание, что jclass в качестве второго параметра передается тогда, когда метод объявлен как static. Если бы он был обычным методом, то нам бы передавался jobject, который бы идентифицировал объект, метод которого мы вызвали (фактически это аналог this).

Нам остается только реализовать эти функции:

    #include <iostream>
    #include «my_mega_pack_NativeCallsClass.h»
 
 
    JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printOne(JNIEnv *env, jclass myclass)
    {
        std::cout << «One» << std::endl;
    }
 
    JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printTwo(JNIEnv *env, jclass myclass)
    {
        std::cout << «Two» << std::endl;
    }

Давайте теперь реализуем более сложное поведение. Пусть у нас будет 2 метода: inputInt и outputInt. Один из них будет считывать число с консоли, а второй — выводить. Наш java класс будет выглядеть так:
package my.mega.pack;
 
public class NativeCallsClass
{
    static
    {
        System.loadLibrary(«megalib»);
    }
 
    native public static int inputInt();
    native public static void outputInt(int v);
}
Запускаем javah и видим, что сигнатуры методов несколько изменились. Теперь они такие:
JNICALL Java_my_mega_pack_NativeCallsClass_inputInt(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_outputInt(JNIEnv *, jclass, jint);
jint — это typedef. Фактически он обозначает некоторый примитивный тип (например, int), который соответствует int в java. Как видим, задача оказалась не на много сложнее предыдущей 🙂 Наши функции будут выглядеть так:
#include <iostream>
#include «my_mega_pack_NativeCallsClass.h»
 
JNIEXPORT jint JNICALL Java_my_mega_pack_NativeCallsClass_inputInt(JNIEnv *env, jclass myclass)
{
    int ret;
 
    std::cin >> ret;
 
    return ret;
}
 
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_outputInt(JNIEnv *env, jclass myclass, jint v)
{
    std::cout << v << std::endl;
}

Итак, в первой части мы рассмотрели, как работает JNI, как писать java классы, с помощью которых можно осуществлять вызовы нативного когда и как писать C++ функции, вызываемые через JNI. В следующей части (или частях) мы рассмотрим взаимодействие с JVM из C++ кода, работу с классами, объектами полями и методами, создание proxy классов java, которые бы представляли C++ классы и запуск JVM из C++ кода.

Естественно, продолжение будет только в том случае, если это кому-то интересно 🙂

Мой отчет по годовому изучению платформы Java EE / Habr

Этот пост – отчет по результатам годового изучения платформы Java EE.
Он будет полезен мне своей отчетностью. Может быть пост будет полезен тем читателям хабра, которые задумали учить Джаву.


Итак. В ноябре 08 года я решил научиться программировать на Java.
Исходные данные:

  • опыт работы в телекоме,
  • небольшой опыт программирования на php.
Цель: стать веб-разработчиком на Java.
Перый этап – знакомство.

После непродолжительного гугления на тему решил начать изучение с Википедии.

Статьи дали мне представление о языке и платформе в первом приближении. Почти всё, кроме общих фраз, было мне не понятно.
Второй этап – выбор книг для обучения Java SE.

В качестве учебника я решил выбрать Thinking in Java, Fourth Edition by Bruce Eckel. О выборе книги не пожалел. Книга очень легко читается, к ней можно возвращаться не один раз, все более углубляясь в язык. Я завел большую тетрадь и стал выписывть в нее куски кода, определения, ошибки, с которыми сталкивался во время изучения языка.

Среда разработки

-Добрый день. Подскажите пожалуйста хорошую среду разработки для Java.
-Среда на следующей неделе будет очень хорошей для разработки на Java.

Первые несколько месяцев я, следуя совету Брюса Эккеля, не использовал никаких IDE. После того, как уже набил руку, печатая public static void main(String args[]),
я решил поставить Eclipse. Мелочиться не стал – скачал сразу ЕЕ-сборку (Eclipse IDE for Java EE Developers).
Третий этап – программы «Hello world».

Подойдя к изучению стандартных библиотек я понял, что мне скучно набивать примеры из учебника, нужно набросать себе несколько задач, которые я уже смогу решить средствами Java SE. Мои выполненные задачи:
0. Написать пятнашки. (Вдохновился на сайте www.javenue.info/post/42)
1. Создать утилиту Pinger – мониторит состояние доступности узлов сети. Выводит таблицу доступности.
2. Создать утилиту Chrome Bookmarks Reader – для простого импорта вкладок из Хрома во внешний xml-файл.

Варианты задач:

Стоит отметить, что на данном этапе я стал искать другие ресурсы для продолжения обучения. Глоток свежего воздуха мне дал сайт Антона Сабурова — java-course.ru. Он позволил систематизировать уже имеющиеся сведения о Java SE.

Четвертый этап – знакомство с web.

В качестве ресурса для знакомства с web-программировнием на Java я решил выбрать материалы javapassion.com. Эти онлайн-курсы ведет кореец Sang Shin, работающий в компании Sun. На сайте имеются расписания курсов, их содержание, домашние задания. Помимо знаний, дополнительный мотиватор к прохождению курса – «сертификат выпускника», который Санг Шин вывешивает у себя на сайте.
Пятый этап (текущий) – опять программы «Hello world».

Имея ворох не слишком систематизированных знаний по базовым технологиям платформы EE я решил их упорядочить, применив на практике. Придумал себе задачу:schaanCMS на платформе Java EE.

Базовая CMS:

  1. Система авторизации.
  2. CMS-каркас.
  3. Управление меню.
  4. Разделы разной степени вложенности
  5. Редактор статей
  6. Модуль mini-nagios (мониторинг устройств и сервисов)
  7. Модуль тикетов (обработка заявок клиентов)
  8. Модуль заступления в дежурство (график работы сисадминов)
  9. Поверх CMS – справочный раздел (для сисадминов)

Систему планирую внедрить на работе для использования в повседневной жизни.
Хочу сделать функционал для мониторинга активного сетевого оборудования и сервисов сети (mini-nagios), модуль тикетов для обработки заявок отдела работы с клиентами, справочный раздел для дежурных администраторов.

Шестой этап – работа.

Если на этапе разработки и тестирования система schaanCMS докажет свою необходимость, будем ее внедрять на работе.
Выводы:

  1. Частные. Я смог освоить базовые технологии платформы Java для того, чтобы начать разработку.
  2. Общие. В Гугле достаточно информации для самостоятельного изучения веб-технологий. (:

На этом пока все. Желаю всем успехов в своих начинаниях!

Несколько замечаний:

  1. Стоит отметить, что на протяжении всего обучения я старался активно использовать официальную документацию по JDK, SDK. При возникновении проблем я сначала пытался решить ее прочтением спецификаций к API, а лишь потом лезть в Гугл.
  2. В начале обучения я стал жертвой рекламы и купил «книгу известного гуру в области программирования», Герберта ШилдтаПолный справочник по Java, 7-е издание. Как по мне, книга отличается плохим переводом и недостаточно яркими примерами. Ну а как справочник лучше уж использовать официальную документацию. Так-то.
  3. У меня бывали моменты, когда мне казалось, что я выбрал неправильный путь и что Джава слишком запутана и сложна. Я банально терял интерес к обучению. Тогда мне на помощь приходили замечательные видео-курсы Стэнфордского университета CS106A (видео лежит на youtube). Курсы ведет такой заводной мужик, что хочется забыть о сложностях программирования. Подсказка от кэпа: SUN == Stanford University Network.
  4. Для изучения Spring решил читать — vaannila.com. Просто и понятно.
  5. Для окончательной и бесповоротной систематизации языка я решил через пару месяцев сдать экзамен на сертификат SCJP.
    Цель: знать язык в совершенстве.
    Мотивация к изучению: красивый сертификат.
    Мотивация к качественному изучению: придется зубрить много материала, так как провалить экзамен, стоящий $300, не хочется.
Замечания хабрапользователей:

  1. Для подготовки к SCJP и для систематизации знаний в SE можно использовать бесплатный и в то же время качественный симулятор от ExamLab.
  2. Помимо свободных сред разработки NetBeans и Eclipse хабрапользователи рекомендуют присмотреться к коммерческой IntelliJ IDEA. Она бесплатная для разработки на Java SE и за нее нужно платить денюжку, если хочешь работать с EE (хотя в данном случае могут быть нюансы).

    «IDEA, в отличие от остальных, построена не как «tool for geeks», не заставляет вас думать «куда бы тут ткнуть», подсказывает как сделать лучше. Новички учатся значительно, в разы, быстрее именно с ней». (#)

    «Eclipse принято советовать новичкам, поскольку он бесплатен. В нем все супер до тех пор, пока вы имеете дело с «чистым» джава-кодом, отладкой его и всем, что не выходит за рамки запуска единственного проекта с main(). Как только вы пытаетесь запустить произвольный сервер приложений, или хотите для фреймворка ХХХ пользоваться мастером (тем более, производить рефакторинг кода, написанного с использованием такого фреймворка), начинается геморрой, поскольку плагины либо написаны криворукими бесплатными разработчиками, либо плохо совместимы с текущей сборкой, либо не работают друг с другом и так далее и тому подобное. Мой опыт говорит, что чем потом терять в производительности, лучше сразу переучиться на IDEA. Достичь ее пределов довольно сложно, а глюки, если они и бывают, некритичны и исправляются быстро». (#)

  3. Очень хорошие и доступно написанные статьи для начинающих Java-разработчиков живут по адресу skipy.ru
  4. По поводу разработки своей CMS. Высказаны мнения, что объем работ по её написанию достаточно большой, а в выборе верной архитектуры очень легко ошибиться (ввиду отсутствия опыта). Поэтому вопрос разработки CMS остается открытым. Возможно я ограничусь написанием небольших веб-ориентированных утилит, каждая из которых будет использовать ту или иную технологию и выполнять несложную задачу.
  5. По поводу устройства на работу и знания фреймворков. Более опытные камрады говорят, что не стоит углубляться в веб-фреймворки для Джавы. Стоит знать в общих чертах, что из себя представляют JSF, Struts, Spring, Hibernate и для каких целей используются. Упор в подготовке лучше сделать на детальное изучение сервлет-контейнеров (Tomcat, Jetty) и веб-сервисов.
  6. Касаемо знания вспомогательных технологий и инструментов.
    «Проект на Джаве – это, чаще всего, не один человек и даже иногда не одна команда. Я всегда обращаю внимание на то, упомянуты ли в резюме «инфраструктурные» инструменты — особенно Maven (или, хуже, Ant), умеет ли товарищ писать тесты, понимает ли, как произвести нагрузочное тестирование и как сделать из него правильные выводы, знаком ли с инструментами ведения проекта, например, багтрекером JIRA, знает ли SVN (попадаются и незнающие), может ли пользоваться Линуксом (хотя бы перезапустить Томкет и посмотреть, не кончилось ли место на диске). Очень хорошо посвятить некоторое время изучению java reflection (потому что это один из ключей к пониманию того, как jvm работает). На мой взгляд, тонкости владения языком совершенно не важны, если конечный продукт не умеет собираться с консоли или вы не можете обеспечить работу среды, в которой он будет выполняться. Использование правильных инструментов — это половина успеха, другая половина — это опыт. Нюансы, касающиеся именно программирования (некритичные для работоспособности, типа, как покрасивее написать) вообще не важны». (#)

Спасибо всем учавствовавшим в обсуждении статьи. Ваши комментарии очень ценны для меня и для остальных таких новичков, как я.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *