• 3.31

  • +16.09

  • ,

Реализация стратегии.

Судя по записям задач поиска контуров, brainfuck кодирования многим интересна алгоритмика, как одна из сторон программирования.
Предлагаю вам реализовать «мозги» следующей игры.
И если, авторов решения будет несколько, можно устроить «соревнование».
  • ,

Константы и интернационализация

При работе программа опирается в вычислениях или вводе-выводе используется не только переменные, но и явно прописанные константы. Допустим Вы обрабатываете финансовые данные и по ходу кода несколько раз выводите результаты (пусть в консоль) примерно таким образом:
System.out.println("Итоговое количество =" + countLoans);
...
String strCount = "Итоговое количество :" + loans.calc(); 
...
System.out.println(strCount + strSumDesc);
И это весьма плохо по нескольким причинам.
Причина 1. Нарушение принципа DRY (Don’t repeat yourself — не повторяй).
Дублирование текста и, как следстие, увеличение размеров скомпилированного кода. Также при этом возможны описки.
Причина 2. Нарушение принципа KISS (keep it simple stupid — делайте вещи проще).
В случае необходимости изменения такого рода констант придется прошерстить весь код.
Причина 3. Каждое изменение константы потребует перекомпиляции кода.
Причина 4. Ввод мультиязычности потребует (при таком построении кода) много дополнительных конструкций к каждой такой константе.

Первые две причины устранит ввод констант с модификаторами static final и их инициализацией + применение их по ходу кода. Т.е. создаем
public class Constants {
    public static final int COUNT_DEPARTMENTS = 20;
    public static final String MSG_TOTAL_AMOUNT = "Итоговое количество";
    ...
}
и применяем примерно как
System.out.println(Constants.MSG_TOTAL_AMOUNTCOUNT_DEPARTMENT + "= " + countLoans);
Если нам необходимо поменять фразу, мы делаем только в одном месте.

Чтобы устранить третью причину можно воспользоваться классом Properties, более подробно есть на соответствующих лекциях JavaRush и некоторых big-задачах второй половины 20-х уровней. Наиболее часто употребляемые переменные можно вынести как отдельные public static final, реже используемые —
получать через вызов метода PROPS.getProperty().
public class Constants {
    public static final int COUNT_DEPARTMENTS;
    public static final String MSG_TOTAL_AMOUNT;
    public static final Properties PROPS;

    static {
        Logger log = LogManager.getLogger(Constants.class);
        PROPS = new Properties();
        try{
            fis = new FileInputStream("confs/config.properties");
            PROPS.load(fis);
            fis.close();
        } catch ( IOException e) {
            log.error(e.getMessage());
        }
        MSG_TOTAL_AMOUNT = PROPS.getProperty("msg.total.amount");
        COUNT_DEPARTMENTS = Integer.parseInt(PROPS.getProperty("count.departments"));
        ...
    }
}
Осталось разобраться с интернационализацией. Сделаем так, чтобы в static блоке происходила загрузка нужно файла properties. Для этого будем работать с ResourceBundle. Отметим, что из ResourceBundle стандартными методами можно считывать значения ключей, но не устанавливать их.
Поэтому по прежнему храним в отдельном Properties (config.properites) файле параметры, не имеющие отношение к интернационализации (к тому же некоторые из низ возможно менять по ходу действия программы). Например, язык интерфейса. Удобный интерфейс IDEA нам поможет.
Создаем его:

Добавляем нужные языки:

В структуре проекта для IDEA отображается как:

Наполняем содержимым:


public class Constants {
    public static final int COUNT_DEPARTMENTS;
    public static final String MSG_TOTAL_AMOUNT;
    public static final Properties PROPS;
    public static final ResourceBundle UI_LANGUAGE;

    static {
        Logger log = LogManager.getLogger(Constants.class);
        PROPS = new Properties();
        try{
            fis = new FileInputStream("confs/config.properties");
            PROPS.load(fis);
            fis.close();
        } catch ( IOException e) {
            log.error(e.getMessage());
        }
        if (PROPS.getProperty("ui.language").equalsIgnoreCase("RUS"))
            UI_LANGUAGE = ResourceBundle.getBundle("messages", CharsetControl.RUS);
        else
            UI_LANGUAGE = ResourceBundle.getBundle("messages", CharsetControl.ENG);
        
        MSG_TOTAL_AMOUNT = UI_LANGUAGE.getString("msg.total.amount");
        COUNT_DEPARTMENTS = Integer.parseInt(PROPS.getProperty("count.departments")); 
        ...
    }
}

Мы добились, что язык интерфейса меняется без вмешательства в код. К тому же мы получили четкое разделение труда: можем отдать русский properties-файл переводчику-гуманитарию, который в удобном для себя текстовом редакторе его переведет. Нам останется только переименовать его в нужное имя файла и разместить обратно в ResourceBundle.

Небольшое дополнение (использование CharsetControl):
Properties файлы не чувствительны к кодировке (обрабатывают содержимое файла исключительно по одному байту, что равносильно использованию ISO8859-1) и кириллица в Properties-файла будет отображена некорректно. Чтобы избежать этого, можно:
— Перегонять кириллические символы в ESCAPE-последовательности \uxxxx через native2ascii
— Помочь Properties передав ему необходимый ResourceBundle.Control, в котором нам необходимо перегрузить метод (код нашел на просторах интернета + немного модифицировал).
Возможно кому то поможет:
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.*;
import java.util.*;

public class CharsetControl extends ResourceBundle.Control {

    public static final CharsetControl UTF_8 = new CharsetControl("utf8");
    public static final CharsetControl RUS = new CharsetControl("cp1251");
    public static final Locale    ENG = new Locale("en","GB");

    private Charset charset;

    public CharsetControl(String charset) {
        this(Charset.forName(charset));
    }

    public CharsetControl(Charset charset) {
        this.charset = charset;
    }

    public Charset getCharset() {
        return charset;
    }

    @Override
    public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
            throws IllegalAccessException, InstantiationException, IOException {

        String bundleName = toBundleName(baseName, locale);
        ResourceBundle bundle = null;
        if (format.equals("java.class")) {
            try {
                Class<? extends ResourceBundle> bundleClass  =
                        (Class<? extends ResourceBundle>)loader.loadClass(bundleName);

                if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
                    bundle = bundleClass.newInstance();
                } else {
                    throw new ClassCastException(bundleClass.getName()
                            + " cannot be cast to ResourceBundle");
                }
            } catch (ClassNotFoundException e) {
            }
        } else if (format.equals("java.properties")) {
            final String resourceName = toResourceName(bundleName, "properties");
            final ClassLoader classLoader = loader;
            final boolean reloadFlag = reload;
            InputStream stream = null;
            try {
                stream = AccessController.doPrivileged(
                        new PrivilegedExceptionAction<InputStream>() {
                            public InputStream run() throws IOException {
                                InputStream is = null;
                                if (reloadFlag) {
                                    URL url = classLoader.getResource(resourceName);
                                    if (url != null) {
                                        URLConnection connection = url.openConnection();
                                        if (connection != null) {
                                            connection.setUseCaches(false);
                                            is = connection.getInputStream();
                                        }
                                    }
                                } else {
                                    is = classLoader.getResourceAsStream(resourceName);
                                }
                                return is;
                            }
                        });
            } catch (PrivilegedActionException e) {
                throw (IOException) e.getException();
            }
            if (stream != null) {
                try {
                    bundle = new PropertyResourceBundle(new InputStreamReader(stream, getCharset()));
                } finally {
                    stream.close();
                }
            }
        } else {
            throw new IllegalArgumentException("unknown format: " + format);
        }
        return bundle;
    }
}

  • ,

Алгоритмические задачи

Сильный конек JavaRush — изобилие практики, что сильно способствует закреплению данного на лекциях материала. Здесь 95% задач с уклоном в практику написания прикладных или серверных программ.
Это здорово закрепить на практике поданные на лекциях материалы (коллекции, дженерики, массивы, многопоточность и т.п.) или поэтапно создавать и рефакторить задачи со множеством классов и их взаимодействия (big-и). Как глоток свежего воздуха встречаются алгоритмические задачи (все, наверное, вспоминают последний бонус 20го уровня, построения бинарного дерева-списка), но их число не велико. Оно и понятно, курс посвящен прикладной практике, а не алгоритмике (которая требует гораздо больше теории кибернетики и математики).
Тем, кто в чем то похож на меня в плане иногда немного отвлечься и потренировать больше математические навыки в программировании, рекомендую codeforces.com/. Тамошние задачи можно решать (помимо прочих языков программирования) и на Java 7/8.
Пул задач (помимо них устраиваются и соревнования), доступен на codeforces.com/problemset. Помимо них есть и соревнования и т.п.

100% принимает решение в единственном java-файле c использованием пакета по умолчанию (то есть нет java-файле указания package) и точкой входа public static void main(String[] args). Другие варианты отправки решения я не пробовал.

Приведу пример самой простой задачи ( codeforces.com/problemset/problem/1/A ) с решением (воспринимайте как тестовый пример), чтобы могли использовать как болванку при решении других:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class task1A {
    /*
        Театральная площадь в столице Берляндии представляет собой прямоугольник n × m метров.
        По случаю очередного юбилея города, было принято решение о замощении площади 
        квадратными гранитными плитами. Каждая плита имеет размер a × a.

        Какое наименьшее количество плит понадобится для замощения площади? Разрешено покрыть 
        плитами большую поверхность, чем театральная площадь, но она должна быть покрыта обязательно. 
        Гранитные плиты нельзя ломать или дробить, а разрешено использовать только целиком. 
        Границы плит должны быть параллельны границам площади.

        Входные данные
        В первой строке записано три целых натуральных числа n, m и a (1 ≤ n, m, a ≤ 10^9).

        Выходные данные
        Выведите искомое количество плит.
    */
    public static void main(String[] args) throws IOException {
        BufferedReader reader  = new BufferedReader(new InputStreamReader(System.in));
        String[] s=reader.readLine().split(" ");
        long n = Long.parseLong(s[0]);
        long m = Long.parseLong(s[1]);
        long a = Long.parseLong(s[2]);
        reader.close();

        long cntSide1 = n/a + (int) Math.signum(n%a);
        long cntSide2 = m/a + (int) Math.signum(m%a);
        System.out.print(cntSide1*cntSide2);
    }
}


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

P.S. Если администрация сочтет рекламой стороннего ресурса — можете удалить. Хотя лично я считаю, что дополнительная зарядка мозгам в плане алгоритмики не помешает.
P.S.S. Если есть другие подобные ресурсы — пишите в комментариях (лучше в развернутых — плюсы, минусы и примеры решенных тестовых задач для легкого старта другим).
  • ,

Учим английский (навыки чтения). Уровень 1.

Все мы учили English, как говорится, хоть где-нибудь и хоть как-нибудь.
Сам я в школе и институте прошляпил английский (хотя получил по нему отлично, так как помог в каникулы с ремонтом класса в школе — красил, шпаклевал и т.п., а в институте в лихие 90-е некоторым преподам было банально не до нас).

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

В конце данного топика я приведу классический текст адаптированной сказки «Кот в сапогах» Шарля Перро (куда же мы денемся без котиков). Вы должны его с листа читать как русский текст, так как:
  • словарный запас мал, соответствует уровню Starter/Elementary (Total words: 2665 Unique words: 560)
  • содержание текста, надеюсь, всем знакомо :)

Вероятно, пару-тройку незнакомых слов, могут встретить и люди со средним знанием english-а, так, что и им, возможно, будет полезно прочитать эпическую фентези про котэ-победителя гигантов.
Кому тяжело — учите встретившие слова и разбирайте грамматику.
Слова лучше заносить в онлайн-тренинги английского (я, например, предпочитаю, lingualeo.ru, который имеет plugin позволяющий заносить прямо из браузера, тыкнув на незнакомое слово) и тренировать там периодически. Аналогично многие сайты предлагают и тренинг грамматики.

Если интересно, могут периодически выкладывать и другие тексты (со временем и технические, возможно адаптированные). Чтобы JR-шовцы могли периодически тренировать навык перевода. Напишите в комментариях, стоит ли.

P.S. К администрации, текст сказки взят отсюда: english-e-books.net (graded readers ebooks – best choice to learn English. Download or read online for free! ), так что, думаю проблем с нарушением юрид прав нет.

Собственно текст.
Charles Perrault. Puss in boots.
CHAPTER ONE
Once upon a time there was a poor miller. He lived in a small house, together with his three sons. The miller worked at the mill, and his sons helped him. The miller had no horse. He used his donkey to bring wheat from the fields.
The years went by. The miller grew old and died. His sons decided to divide their father's things among themselves. That was easy: he had almost nothing to leave to his sons. Only his mill, his donkey and his cat.
«I'm going to take the mill,» said the miller's oldest son.
«I'm going to take the donkey,» said the second.
«And what about me?» asked the youngest son.
«You? You can take the cat,» laughed his brothers.
The young fellow was very much upset. He went out of the house and sat down on the bench.
«Oh, well,» he said in a sad voice. «My brothers have the mill and the donkey. They can put them together and make enough money to live an honest life. But what can I do? I can eat the cat, and I can
make a hat out of his fur. But then I have nothing. I can die of hunger.» The Cat was sitting on the bench too. He was trying not to listen to his master. But of course he heard all his words. And he didn't like them at all. He put on a serious face and said:
«Don't look so sad, Master. I'm not a bad thing. And I am more useful to you alive than dead. I can prove that.»
«How so?» asked the Cat's master. «All you have to do is to give me a bag, and get a pair of boots. Such as gentlemen wear in the woods. I'm going to show you that you're lucky to have me.»
«It's unusual that a cat can speak at all,» the miller's son said to himself. But then he started thinking. «This cat's very good at catching rats and mice. He played so many cunning tricks on them. He never came home without a rat or a mouse. He could hide in the wheat, or pretend to be dead. Perhaps, he can help me after all.»
«OK,» he said to the Cat. «I'm going now to order the boots.»

CHAPTER TWO
The miller's son went to the best shoemaker in town. The shoemaker made elegant shoes and boots for gentlemen.
«I want to order a fine pair of very small boots. They are for my cat,» said the young man.
«OK,» said the shoemaker.
He was not surprised at all. Or, perhaps, just a little.
«Do have any money?» he asked.
«Here you are,» said the miller's son.
He took his last silver coin out of his pocket and gave it to the shoemaker.
Soon the boots were made. The Cat pulled them on. He looked very nice in his elegant boots.
«Don't worry about the money, Master. I'm going to bring you luck,» said the Cat in Boots.
He put the bag around his neck. Cats have no hands, so he held the strings in his forepaws. Then he put some vegetables and a piece of bread into the bag.
The Cat went to the woods. There were many rabbits there. So he lay down, pretending to be dead. The
Cat didn't move at all. His plan was to wait for some foolish rabbit to come and look into his bag.
The Cat didn't wait long. Soon a foolish young rabbit put his head inside the bag. The Cat closed the strings at once and caught him. Then, very proud, he went with the bag to the palace and asked to speak with the King.
The King agreed to see the unusual visitor. The Cat went upstairs to the King's room. He came up to the King and made a low bow. Then he said:
«Sir, here is a nice rabbit from the lands which belong to my noble master, the Marquis of Carabas (he decided to give his young master this title). He told me to offer it to Your Majesty.»
«Tell your master,» said the King, politely, «that I thank him for this nice present. I'm very pleased with his attention.»
Another time the Cat went to a wheat field and hid among standing wheat. He again held his bag open. Soon two fat partridges ran into the bag. The Cat drew the strings, and caught them both.
The Cat went to the King's palace again. He gave the partridges to the King, with the same message from his master as before. The King received the gift. His majesty was very pleased. He even ordered to take the Cat down into the kitchen and give him something to eat and drink.
The Cat enjoyed the meal very much. He sat in the kitchen for a long time, talking about his rich master, the Marquis of Carabas.

CHAPTER THREE
The Cat in Boots continued for two or three months to bring presents to the King. Every time he came to the palace and said, «Your Majesty, here's another present from my master, the Marquis of Carabas.»
Then one day one of the Cat's friends in the palace said to him, «The King wants to drive today in his carriage along the bank of the river. His Majesty is going to take his daughter with him.»
The young princess was a very beautiful girl.
The Cat in Boots said to his master:
«Now you must do what I tell you.»
The miller's son knew nothing about the Cat's new plan. But he was sad and unhappy. That's why he agreed at once.
«Be it so,» he said. «Tell me about your plan.»
«I don't ask much,» said the Cat, looking wise, as cats can. «All you must do is to go and bathe in the river. I know a good place. Then leave the rest to me. Only remember that you are no longer yourself, but the Marquis of Carabas.»
«OK,» said the miller's son, «it's all the same to me.»
He went to the bank of the river, took off his clothes and went bathing. The Cat followed his master and hid his clothes under a great stone.
At that moment the King drove past that place in his carriage. The Cat began to shout at the top of his voice:
«Help! Help! The Marquis of Carabas is drowning!»
The King heard the shouts and put his head out of the carriage. He recognized the Cat.
«Guards!» he said. «Help the Marquis of Carabas! Quick!»
The guards ran to the river. They quickly pulled the young man out of the water.
The Cat came up to the King's carriage. He made a low bow and gave his explanations.
«My master went bathing,» he said, «and suddenly some thieves came. They took all his clothes and ran away. And now the Marquis of Carabas can't appear before Your Majesty and your beautiful daughter.»
«Oh, it's not a problem at all,» said the King.
He ordered one of his guards to ride back to the palace and bring fine clothes for the Marquis of Carabas.
Soon the guard brought a nice suit for the miller's son. The young man put it on and came up to the carriage to thank his majesty.
The miller's son wasn't rich, but he was a handsome and well-built fellow. In the King's elegant suit he looked like a real gentleman.
The beautiful princess admired the handsome young man very much. The miller's son looked at the girl several times, and she fell in love with him.
The King was very happy to meet the Marquis of Carabas. He asked the young gentleman to sit with him and his daughter in the carriage. Of course, the miller's son didn't refuse.

CHAPTER FOUR
The Marquis of Carabas entered the carriage. The Cat in Boots ran away fast. He kept a long way ahead of the King's carriage. He went on and on, till he saw some mowers in a meadow. The Cat came up to them.
«Listen, good people,» he said, in a very firm voice, «the King is going to stop here and talk to you. You must tell him that this meadow belongs to the Marquis of Carabas. Do that if you want to stay alive.»
The King's carriage drove up to the meadow.
«What a nice meadow, and so much hay! Whose meadow is this?» he asked the mowers.
«It belongs to the Marquis of Carabas, sir,» they all cried with one voice, trembling with fear.
«You have a good meadow, marquis,» said his majesty to the miller's son.
The young man bowed and said, «As you see for yourself, this is a very good meadow, sir. The crops of hay are high every year.»
The Cat went still on. He was far ahead of the King and his companions. Finally he came to a wheat field. There were some reapers on the field.
«My good fellows,» he said to the reapers, «the King is going to stop here and talk to you. You must tell him that this field belongs to the Marquis of Carabas. Do that if you want to stay alive.»
The King's carriage arrived a few moments later.
«What a beautiful wheat field! Good people, whose field is this?» he asked the reapers.
«It belongs to the Marquis of Carabas, sir,» cried the reapers, trembling with fear.
At this the King was pleased with the Marquis more than ever.
The King continued his journey, and the Cat still ran on ahead of him. He said the same thing to everyone. The King drove past a new mill, a beautiful garden, houses, more fields and meadows. And all the people on his way said the same: all those places belonged to the Marquis of Carabas.
Finally the Cat returned to the King. He came up to the carriage and bowed.
«Your master is a very rich man,» said the King to the Cat. Then he smiled to the young man and said, «My dear Marquis, isn't this your castle in that park? It looks beautiful. Can we go there now?»
The miller's son didn't know what to say. He looked at the Cat. The Cat bowed and said:
«My master, the Marquis of Carabas, is happy to invite you to the castle. But, Your Majesty, please wait an hour. I'm going to the castle at once to get everything ready for you.»
«No problem,» said the King. «And in the meantime we can visit your nice park. My dear Marquis,» he said to the young man, «I hope you have many flowers there. My daughter is so fond of white roses.»
CHAPTER FIVE
The Cat in Boots ran to the castle. It stood in the middle of a big park. There were wonderful flowers everywhere in the park.
The castle itself was a beautiful high building with small towers. The roofs were red. The walls of the castle were made of white stone. The windows were narrow but high. The rooms on the ground floor had the highest windows. And through the glass visitors could see elegant green curtains.
The castle belonged to a giant. He was the richest giant in the country. All the fields, meadows and woods around the castle were part of his lands. Many people worked for him.
The Cat already knew a lot about the Giant. He was a cruel man. Everyone was terribly afraid of him.
The Cat put on a brave face and went to the castle with his boots on. Soon he arrived at the gate. He asked a servant to speak to the Giant.
«I am a traveller,» he said, «I was not far from here. I couldn't go so near the castle of such a noble gentleman without meeting him.»
The Giant heard this message and agreed to see the visitor. He was going to have dinner, so he was in a good mood.
«Please sit down and have dinner with me,» he said to the Cat.
«Thank you, sir,» said the Cat. «But first I hope you can answer a question. They say that you can change yourself into any animal. A lion, for example, or an elephant.»
«That's true,» said the Giant. «And I can prove it just now. Look! Whom do you see now?»
And the Giant changed himself into a big lion.
The Cat was terribly frightened. He even climbed up the curtain. Of course, it wasn't easy to climb in boots. But the lion was so big! How can you be brave and keep still near such an awful animal?
A few moments later the lion changed back into the Giant. The Cat came down.
«I'm so sorry, sir. I was very frightened,» he said. «But do you know what I think? It was easy for such a big gentleman as you to change yourself into a large animal. But I'm afraid even you can't become a small animal, such as a rat or a mouse. It's just impossible.»
«Impossible!» cried the Giant, very angry. «See how it's impossible!»
At the same moment he changed himself into a mouse. The mouse began to run about the floor.
It was part of the Cat's plan. He jumped on the mouse at once and ate it. So that was the end to the Giant.

CHAPTER SIX
Meanwhile the King, his daughter and the Marquis of Carabas arrived at the castle. The carriage drove over the drawbridge with a loud noise. The Cat heard the noise and ran out into the yard to meet the visitors.
«Welcome, sir, to the castle of the Marquis of Carabas,» he said in a loud voice.
«Your castle is so beautiful, Marquis,» said the King. «Nothing could be finer than this yard and all these buildings. It's not a castle at all. It's a real palace. Let's go inside and look around, if you don't mind.»
The King got out of the carriage and went to the door. The Marquis, without speaking, gave his hand to the princess. As soon as she got out of the carriage, they followed the King.
The King and his companions went through several beautiful rooms and came into a great hall. In the middle of the hall they saw a long table. It was ready for dinner. There were a lot of wonderful things on the table. All of them were the Giant's favourite dishes.
«Why don't we have dinner now?» said the Marquis. «Please sit down.»
The guests took their places. Everyone was hungry, so the dinner started at once.
The King was happy. His daughter was happy too. The girl, in fact, was very much in love with the handsome and polite young man.
His majesty was a practical man, too.
«The marquis is a noble gentleman. My daughter likes him, and he's rich. I think he can make a good husband for my daughter,» he thought after his sixth or seventh glass of wine.
Soon the dinner was over. The King looked across the table at the miller's son.
«Do you like my daughter, marquis?» he said.
«Yes, sir,» said the young man.
«You can marry her then. If you want, of course,» said the King. «That's your choice.»
«I am happy to do that,» said the Marquis of Carabas.
The princess's happy eyes said the same.
The miller's son married the King's daughter the next day. The young man was happy. He was a rich noble gentleman now. The Giant's castle belonged to him. He had all the Giant's lands, too. And he had a beautiful wife.
The Cat in Boots became at once a great lord. Of course, he stayed in the castle with the marquis and the princess. He ordered more elegant boots for himself. But he never ran after mice any more, except for pleasure.
  • ,

Критическое обновление IntelliJ Idea

День добрый,
На всякий случай, если не все подписаны на новости от IntelliJ:

Security update for IntelliJ-based IDEs v2016.1 and older versions

Posted on May 11, 2016 by Eugene Toporov
We have just released an important update for all IntelliJ-based IDEs. This update addresses critical security vulnerabilities inside the underlying IntelliJ Platform. The vulnerabilities, in various forms, are also present in older versions of the IDEs; therefore, patches for those are also available.

While we have had no reports of any active attacks against these vulnerabilities, we strongly recommend for all users to install the update as soon as possible.

Прочие подробности тут: blog.jetbrains.com/blog/2016/05/11/security-update-for-intellij-based-ides-v2016-1-and-older-versions

P.S. На всякий случай сделайте копию (перед обновлением) папки с проектами.
  • ,

Можно ли сделать следующее при планируемом update/redisign сайта?

У меня и многих были ощущения решив и сдав очередную задачу, что схалтурил:
вагон лишних и неоптимальных if-ов на возможные проверки данных для выполнения, километровые простыни кода, хитро*опые ходы (вместо сортировки ручками в цикле взяли и вызвали Collections.sort() ) и т.п. И чувствуешь, что решение более элегантно и правильное.

Вопрос:
Можно ли запланировать сделать следующим образом (варианты):
1) Создать 40 закрытых групп по уровням, доступ на уровень ученик получает при условии, что ВСЕ задачи данного уровня решены им. В уровне можно публиковать решения задач, доказывать оптимальность своего, смотреть как решили другие. Думаю, было бы весьма полезно. Технически наверное сложно сделать (отслеживать авторизацию на ресурсе обучения), но возможно.
2) Или сделать 4 группы (10,20,30,40) и вручную по запросу и проверке добавлять (хлопоты админам форума, но может желающих будет и немного).

Можем есть и другие идеи. Пишите.
  • ,

==Городские легенды== JavaRush

Так как связано с решением задач, добавляю именно в данный тред.

Проверенные лично факты:
1. Неиспользуемые import-ы ведут к провалу попытки.
2. Незакрытые по завершению работы с ними потоки (и другие ранее открытые ресурсы) также ведут к провалу попытки.
3. Конструкции, содержащие анонимные классы, которые в дальнейшем явно не закрываются могут (не всегда!) привести к провалу, например вместо:
FileInputStream iStrm = new FileInputStream( new BufferedReader(new InputStreamReader(System.in)).readLine());
лучше написать гарантированное:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
FileInputStream inputStream = new FileInputStream( reader.readLine());
чтобы в дальнейшем закрыть ДВА потока, а не один, как в верхнем примере.
Хочется уточнить у администрации следующее:
1. Часто в форумах при провале попыток сдачи есть совет убрать пользовательские комментарии — они могут быть причиной провала? Это так, серьезно?! Пока (первые 20 уровней — не сталкивался с этим, правда и комментировал не так уж часто). На форуме есть даже ответы по типу, что помогло. Проясните, пожалуйста.
2. Попытка сдачи в полночь может привести к провалу (время рестарта сервера).

P.S. Пополняйте своими наблюдениями.
  • ,

World of Bytes 1. Работа с изображениями.

Special for Spiker

До этого я пояснял на сухих примерах. Попросили работу с изображениями — ловите.

Постановка учебной задачи.
Дан графический файл (jpeg, png ...).
Необходимо сделать с ним некие манипуляции и записать результат в другой файл.
Для упрощения рассмотрим три задачи:
— получить негатив изображения
— получить черно-белый вариант изображения (сбросить цветность)
— изменить насыщенность зеленого цвета в изображении.

Заметим, что аналогичным способом мы можем, добавляя новые методы реализовывать и другие задачи:
— увеличить резкость или размытие
— изменить размеры
— повернуть по/против часовой.
— и другие возможность Фотошопа :)
вообщем реализовать любой алгоритм над изображением, насколько у нас хватит фантазии и знания матана (например, распознать количество возможных котиков на картинке).

Немного сухой теории.
Мы рассматриваем растровые изображения (есть еще векторные и другие). То есть, когда файл, помимо собственно заголовка со служебной информацией, хранит прямоугольную матрицу точек. Аналогично экрану современного HD-телевизора у которого разрешение 1920х1080 точек и каждая точка представлена как значения трех составляющих цвета: R(ed), G(reen), B(lue) = Красный, Зеленый и Синий. Эти цвета независимы и данная модель взята из биологии восприятия цвета.
В глазу у нас есть колбочки и палочки. Колбочки трех разновидностей (реагируют на соответствующий одной из трех диапазонов длин волн), палочки «обрабатывают» яркость цвета (амплитуду световой волны).
В модели RGB палочки отвечают за значение составляющей (0 — отсутствие, 255- самый яркий свет), а колбочки — соответственно в какие из R / G / B помещать соответствующую интенсивность.

Например:
Отсутствие света — палочки /колбочки не реагируют и RGB=(0,0,0).
Яркий белый свет — все колбочки реагируют равномерно, палочки фигеют и RGB = (255,255,255).
Серая мышка пробежала — все колбочки реагируют равномерно, палочки реагируют средне и RGB = (127,127,127).
Темно-оранжевый — реагируют R и G палочки, палочки едва откликаются, RGB=(30, 30, 0)


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

Исходное изображение:
Котенок

вызов с параметрами -n kitten.jpg newkitten.jpg создаст картинку:
негатив

вызов с параметрами -b kitten.jpg newkitten.jpg создаст картинку:
черно-белый котенок

вызов с параметрами -gr kitten.jpg newkitten.jpg создаст картинку:
сумерки

Собственно котд.
package com.joysi.byteworld;

import com.sun.imageio.plugins.jpeg.*;
import com.sun.imageio.plugins.png.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import java.awt.image.BufferedImage;
import java.io.*;

public class image {
    public static void main(String[] args) throws IOException {
        CoolImage picture = new CoolImage(args[1]); // загружаем файл изображения
        if ("-n".equals(args[0]))  picture.convertToNegative();
        if ("-g".equals(args[0]))  picture.addColorGreenChannel(-100);
        if ("-bw".equals(args[0])) picture.convertToBlackAndWhite();
        picture.saveAsJpeg(args[2]);
    }

    public static class CoolImage{
        private int     height;             // высота изображения
        private int     width;              // ширина изображения
        private int[]   pixels;             // собственно массив цветов точек составляющих изображение

        public int getPixel(int x, int y)   { return pixels[y*width+x]; }   // получить пиксель в формате RGB
        public int getRed(int color)        { return color >> 16; }         // получить красную составляющую цвета
        public int getGreen(int color)      { return (color >> 8) & 0xFF; } // получить зеленую составляющую цвета
        public int getBlue(int color)       { return color  & 0xFF;}        // получить синюю   составляющую цвета

        // Конструктор - создание изображения из файла
        public CoolImage(String fileName) throws IOException {
            BufferedImage img = readFromFile(fileName);
            this.height = img.getHeight();
            this.width  = img.getWidth();
            this.pixels = copyFromBufferedImage(img);
        }

        // Чтение изображения из файла в BufferedImage
        private BufferedImage readFromFile(String fileName) throws IOException {
            ImageReader     r  = new JPEGImageReader(new JPEGImageReaderSpi());
            r.setInput(new FileImageInputStream(new File(fileName)));
            BufferedImage  bi = r.read(0, new ImageReadParam());
            ((FileImageInputStream) r.getInput()).close();
            return bi;
        }

        // Формирование BufferedImage из массива pixels
        private BufferedImage copyToBufferedImage()  {
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int i = 0; i < height; i++)
                for (int j = 0; j < width; j++)
                    bi.setRGB(j, i, pixels[i*width +j]);
            return bi;
        }

        // Формирование массива пикселей из BufferedImage
        private int[] copyFromBufferedImage(BufferedImage bi)  {
            int[] pict = new int[height*width];
            for (int i = 0; i < height; i++)
                for (int j = 0; j < width; j++)
                    pict[i*width + j] = bi.getRGB(j, i) & 0xFFFFFF; // 0xFFFFFF: записываем только 3 младших байта RGB
            return pict;
        }

        // Запись изображения в jpeg-формате
        public void saveAsJpeg(String fileName) throws IOException {
            ImageWriter writer = new JPEGImageWriter(new JPEGImageWriterSpi());
            saveToImageFile(writer, fileName);
        }

        // Запись изображения в png-формате (другие графические форматы по аналогии)
        public void saveAsPng(String fileName) throws IOException {
            ImageWriter writer = new PNGImageWriter(new PNGImageWriterSpi());
            saveToImageFile(writer, fileName);
        }

        // Собственно запись файла (общая для всех форматов часть).
        private void saveToImageFile(ImageWriter iw, String fileName) throws IOException {
            iw.setOutput(new FileImageOutputStream(new File(fileName)));
            iw.write(copyToBufferedImage());
            ((FileImageOutputStream) iw.getOutput()).close();
        }

        // конвертация изображения в негатив
        public void  convertToNegative() {
            for (int i = 0; i < height; i++)
                for (int j = 0; j < width; j++)
                    // Применяем логическое отрицание и отбрасываем старший байт
                    pixels[i*width + j] = ~pixels[i*width + j] & 0xFFFFFF;
        }

        // конвертация изображения в черно-белый вид
        public void convertToBlackAndWhite() {
            for (int i = 0; i < height; i++)
                for (int j = 0; j < width; j++) {
                    // находим среднюю арифметическую интенсивность пикселя по всем цветам
                    int intens = (getRed(pixels[i * width + j]) +
                                getGreen(pixels[i * width + j]) +
                                 getBlue(pixels[i * width + j])) / 3;
                    // ... и записываем ее в каждый цвет за раз , сдвигая байты RGB на свои места
                    pixels[i * width + j] = intens + (intens << 8) + (intens << 16);
                }
        }

        // изменяем интесивность зеленого цвета
        public void addColorGreenChannel(int delta) {
            for (int i = 0; i < height; i++)
                for (int j = 0; j < width; j++) {
                    int newGreen =  getGreen(pixels[i * width + j]) + delta;
                    if (newGreen > 255) newGreen=255;  // Отсекаем при превышении границ байта
                    if (newGreen < 0)   newGreen=0;
                    // В итоговом пикселе R и B цвета оставляем без изменений: & 0xFF00FF
                    // Полученный новый G (зеленый) засунем в "серединку" RGB: | (newGreen << 8)
                    pixels[i * width + j] = pixels[i * width + j] & 0xFF00FF | (newGreen << 8);
                }
        }
    }
}
  • ,

Побайтовая работа с файлами

Special for Spiker

Начнем'c.
В 18 уровне начались первые задачи побайтного чтения файлов:
Прочитать файл, далее найти минимальные/максимальные байты или вывести в упорядоченном виде и т.п.

Народ тут весьма ушлый. Знают про коллекции и про то, что они могут сортировать, вставлять.
Коллекции — мощный механизм. И многие не применяли их вообще до JavaRush-а.
Оно, конечно, похвально изучать их и пытаться приткнуть куда не попадя.

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

Ввести с консоли имя файла
Считать все байты из файла.
Не учитывая повторений — отсортировать их по байт-коду в убывающем порядке.
Вывести на экран
Закрыть поток ввода-вывода

Пример байт входного файла
44 83 44

Пример вывода
83 44


Мы дополнительно завели переменные startTime и finishTime — чтобы засечь время выполнения программы.
Для вычисления использовал i3-3GHz/8Gb RAM/HDD WD Blue-1Tb/Win7-64/jdk-8u73-windows-x64

(примеры программ в вариантах 1-2 взяты из форума info.javarush, они чуть модифицированы только для сортировки в возрастающем порядке — то есть они РЕАЛЬНЫЕ!!)

Решаем в лоб:
// Вариант 1. Загоняем в коллекцию и сортируем используя ее метод Collections.sort 
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());
        long startTime = System.currentTimeMillis();
        
        ArrayList<Integer> listData = new ArrayList<Integer>();
        while (inputStream.available() > 0) listData.add(inputStream.read());
        inputStream.close();
        ArrayList<Integer> result = new ArrayList<Integer>(new HashSet<Integer>(listData));
        Collections.sort(result);

        while (!result.isEmpty()) {
            System.out.print(result.get(result.size()-1) + " ");
            result.remove(result.get(result.size()-1));
        }

        long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}

Решает все замечательно! Тест (если бы был — прошелся бы на ура).
Но в жизни мало файлов содержащих только строчку «Мама мыла раму».
Давайте скормим нашей программе файл в 46Мб (по нынешним меркам вроде и не особо много).
Что такое, программа выполняется 220 секунд.
Попытка скормить с вечера 1Gb файл (размер MPEG4 фильма не в самом лучшем качестве) не увенчалась успехом.
Программа утром все еще читала — а мне идти на работу уже.

В чем проблема? Наверное в использовании
ArrayList<Integer>
у которого внутри 1 миллиард элементов. Каждый элемент его занимает 16 байт минимум (Заголовок: 8 байт + Поле int: 4 байта + Выравнивание для кратности 8: 4 байта). Итого мы добровольно загоняем в память 16 Gb данных при размере оперативы в 8.

Будем делать лучше. Нырнем в коллекции глубже. И ура, нашлось то, что нам нужно.

Встречаем TreeSet

Это множество:
-не допускает хранение двух одинаковых элементов (а значит мы будем хранить в памяти все 255 элементов, вместо миллиарда!)
-при манипуляциях со своими элементами автоматом упорядочивает (само сортирует — вот он, верх совершенства!)

Получаем:
// Вариант 2. Загоняем в ТreeSet который сам сортирует (лютый win!)
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        byte[] arrBytes = new byte[256];
        long startTime = System.currentTimeMillis();

        SortedSet<Integer> list = new TreeSet<Integer>();
        while(inputStream.available()>0) list.add(inputStream.read());
        inputStream.close();

        while (!list.isEmpty())        {
            System.out.print(list.last() + " ");
            list.remove(list.last());
        }

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}


Имеем на выходе:
46Мб файл 176 секунд.
1Gb файл — 3 часа 5 минут.
Прогресс на лицо. Мы смогли «дождаться» результатов, да и 46Мб файл заметно быстрее обрабатывается.

Идем дальше. Давайте попытаемся отказаться от коллекций (это будет для некоторых мучительно больно). Будем использовать простые массивы (это так примитивно).
Заметим одну важную вещь. Кол-во встречающихся байт можно загнать в массив длиной 256.
Так просто будем увеличивать на единицу соответствующий считанному байту элемент массива.

Массив — побайтно

// Вариант 3. Считываем массив побайтно.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();
        
        while (inputStream.available() > 0) arrBytes[inputStream.read()]++;

		inputStream.close();
        // Выводим отсортированный по байт-коду в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

			long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}


Имеем на выходе:
46Мб файл 158 секунд.
1Gb файл — 2 часа 55 минут.
Опять улучшение, но небольшое. И мы сделали все простыми инструментами. Не использовали микроскоп для забивания гвоздей.

Теперь лирическое отступление.
Вспомним устройство компьютера.
Память ОЗУ (DRAM) где обычно выполняется программа и хранятся переменные имеет высокую скорость доступа, но небольшой размер.
Память на жестком/flash диске (HDD или Flash-накопители) где обычно хранятся файлы, наоборот имеет низкую скорость доступа, но большой размер.
Так что когда мы побайтно читаем 1Gb файл (то есть миллиард раз обращаемся к HDD) — мы тратим много времени на работу с низкоскоростным устройством
(по песчинке перекладываем песок с кузова КамАЗа в песочницу).

Попробуем еще улучшить.

Вывалим сразу ВЕСЬ КамАЗ с песком за один раз!

// Вариант 4. Считываем массив сразу целиком за раз в память.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();
        
        byte fileImage[]=new byte[inputStream.available()];
        long fileSize=fileImage.length;
        inputStream.read(fileImage);
        for (int i = 0; i <fileSize ; i++) arrBytes[fileImage[i] & 0b11111111]++;

		inputStream.close();
        // Выводим отсортированный по байт-коду в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}

небольшое, но опять таки важное отступление
Заметим:
1. индекс у arrBytes определен в пределах 0..255,
2. fileImage — массив байт, элементы которого имеют значение -128..127
Поэтому для подсчета байт будем использовать конструкцию
arrBytes[fileImage[i] & 0b11111111]++;

которая банально сбросит бит знака и вернет нам значение в диапазоне 0..255

И так, результаты:
46Мб файл 0.13 секунд (меньше секунды).
1Gb файл — 9 секунд.
Мы сделали это! Мы невероятно круты! Ускорились с 3 часов до 9 секунд.

Все, можно откинуться в кресле и попить чайку. А теперь еще один эксперимент —
попробуем файл в 32 Gb (например, HD фильм). Получим в результате треск
работающего HDD с вываливанием программы в Windows. КамАЗ вывалив кузов с песком сломал песочницу!

Что будем делать? Вспомним еще один факт. Файлы в ОС хранятся обычно порциями (кластерами) по 2-64Кб
(зависит от типа файловой системы, настроек и т.п.). Будем считывать порциями, для примера в 64000 байт.
Попытаемся разгрузить КамАЗ экскаватором достаточно большими порциями:

Используем буфер.
// Вариант 5. Считываем массив кусками.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();
        
        int  bufferSize = 64000;
        byte buffer[]   = new byte[64000];

        while (inputStream.available() > 0) {
            if (inputStream.available() < 64000) bufferSize = inputStream.available();
            inputStream.read(buffer, 0, bufferSize );
            for (int i = 0; i <bufferSize ; i++) arrBytes[ buffer[i] & 0b11111111 ]++;
        }

		inputStream.close();
        // Выводим отсортированный по байт-коду в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}

В итоге получили:
46Мб файл 0.08 секунд (меньше секунды).
1Gb файл — 0.9 секунд(меньше секунды).
32Gb файл — 31 секунда.

Заметим для 1 Gb файла мы улучшили производительность с нескольких часов до долей секунд!!!

На этом скромном факте закончим эксперимент и улучшение начального кода. Мы достигли прогресса во многом — нас радуют новые показатели расхода памяти и времени работы. Также мы не подтягиваем в данном случае бесполезные коллекции из стандартной библиотеки.

P.S. Кто-то скажет пример надуманный и т.п. Но полно похожих задач — проанализировать огромный объем элементов, имеющих конечное число состояний.
Например изображения (RGB — обычно хранятся в 24 байтах, в нашем случае long[] arrRGB = new long[256*256*256] занял бы в памяти всего 64Мб),
музыка (амплитуда обычно оцифровывается в 16 или 24 бита) или дискретные показатели датчиков и т.п.
  • ,

package com.javarush.test.level18.lesson03.task*

После решения задач люблю смотреть как решали их на форумах.
Все первые пять задач из 18 уровня (task01… task05) на побайтное чтение файлов и их разбор.
Считайте имя файла, найдите максимумы-минимумы и выведите результаты…

Во всех топиках и github репозиториях (те кто собирает свои решения ) используются коллекции:
List (чаще всего), Set-ы и TreeSet-ы (чтобы хранилось в отсортированном порядке). И везде идет чтение очередного байта из файла в List/Set…

Да, все правильно. Решение будет принято. Почти все из нас создадут текстовый файл и поместят в него что-то наподобие «123456123». Некоторые пойдут чуть дальше и натравят код на реальный текстовый файл (например книжку в ASCII). Мы все, пройдем через исправление ошибок перед тем как нажать зеленую галочку для проверки на сервере. Увидим заветное «задача принята» или с досадой увидев что тест не прошел и быстро найдем мелочь в коде программы, наподобие вывода с новой строки вместо вывода через пробел. И побежим дальше решать очередную задачу.

А теперь остановимся. Подумаем. Взглянем на содержимое жесткого диска компьютера. Много ли на нем файлов размеров в 10 байт? Не удивлюсь, если у многих есть файлы и на 20+Gb, например HD фильм. А теперь — натравите ваш код на него… который добавит побайтно 20 миллиардов раз в List и знайте, что каждый элемент List-а занимает или 6 или 12 байт. Сколько, говорите у вас оперативной памяти на ПК?

Некоторые пойдут дальше. И будут в этих задачах использовать TreeSet-ы. У них накладные расходы на элемент всего 32 или 64 байта, зато он убьет дубли (повторяющиеся значения байты в файле). Но не забывайте, что при вставке он будет сортировать свои элементы или искать по красно-черному дереву (способ организации хранения элементов в нем с учетом сортировки). А это не просто запись-чтение. И это будет выполнено 20 миллиардов раз!

Нам сделали упор на коллекциях. О удобстве их применения. Мы потихоньку узнаем о удобных методах, которые нам могут в 2-3 строчки кода найти, отсортировать, отсечь, добавить… Изучать и применять их выгодно.
Но для подобных простых задач они излишни, а в при переносе на возможные реальные объемы данных — бесполезны, а порой и вредны.

Немного отвлечемся от данных задач. Представьте вы на собеседовании. Вам ставят задачу — вывести (не важно, на экран или в файл) отсортированный большой по размеру массив байт (размерности 0 < N < миллиарда) и вывести его. Вопросы:
  • сколько операций циклов вам необходимо и сколько операций сравнения для построения такого массива
  • cколько займет в памяти отсортированный массив?

Правильные ответы: на первый — ни одного сравнения и один цикл, на второй — 1 килобайт (можно и меньше).

Подробности в коде на «отсутствующую» шестую задачу в блоке (по аналогии с первыми пятью, чтобы не было прямого копи-пасты при решениях существующих задач):
/* Сортировка байт
Ввести с консоли имя файла
Считать все байты из файла.
Не учитывая повторений - отсортировать их по байт-коду в убывающем порядке.
Вывести на экран
Закрыть поток ввода-вывода

Пример байт входного файла
44 83 44

Пример вывода
83 44
*/

...
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        // Считываем массив
        while (inputStream.available() > 0) arrBytes[inputStream.read()]++;
        // Выводим отсортированный по байт-коду в обратном порядке
        for (int i = 255; i >= 0 ; i--)
            if (arrBytes[i] > 0) System.out.print(i + " ");

        inputStream.close();
    }


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

Давайте, не будем палить из пушек по воробьям!

P.S. Можете заминусовать или как тут принято за излишнюю резкость, но не смог удержаться.
  • ,

Красиво реализованные задачи.

В рамках курса встретилось вчера задача про ресторан
package com.javarush.test.level17.lesson10.bonus03;
Решение такой задачи довольно простое и сразу видно. Но, закодировано прекрасно: использование Singleton, Queue + разбиение на классы.
В дальнейшем (17+) такое еще будет встречаться?
Будут ли «архитектурные» (с самостоятельным проектированием) задачи?