Комментарии 20
Вы там упомянули SimpleDateFormat, Date и Calendar — не надо этому учить новичков! Пусть сразу используют java.time.*
классы, и спасут и себя и окружающих от головной боли. У старого date/time API очень много подводных камней и неочевидностей, и при наличии JSR-310 нет совершенно никаких причин их использовать, особенно в новом коде.
После ключевых слов перед круглой скобкой принято ставить пробел, чтобы отличать управляющую конструкцию языка от вызова функции.
После закрывающей круглой скобки перед открывающей фигурной скобкой нужен пробел.
Имя final-поля большими буквами нарушает правила именования. Большими буквами обозначаются константы, значение которых никогда не меняется. А final-поле у каждого экземпляра может иметь различные значения.
Runnable — функциональный интерфейс. Начиная, с 8-й явы его не надо анонимным классом реализовывать. Вместо этого надо лямбду создавать.
В иммутабельном классе Vector поля надо было сделать final.
Вместо compute() для модификации объектов в Map в методе inverse() лучше подойдет computeIfAbsent().
Стримы вообще не представлены никак. А это уже 5 лет как ключевое API.
Вот как выворачивание map выглядит на 10-й яве.
<K, V> Map<V, Collection<K>> inverseSequential(Map<K, V> map) {
return map.entrySet().stream().collect(
HashMap::new,
(result, entry) -> {
var value = result.computeIfAbsent(entry.getValue(), k -> new ArrayList<>());
value.add(entry.getKey());
},
(a, b) -> { throw new UnsupportedOperationException(); }
);
}
<K, V> Map<V, Collection<K>> inverseParallel(Map<K, V> map) {
return map.entrySet().parallelStream().collect(
HashMap::new,
(result, entry) -> {
var value = result.computeIfAbsent(entry.getValue(), k -> new ArrayList<>());
value.add(entry.getKey());
},
(map1, map2) -> {
map2.entrySet().forEach(
entry -> {
var value = map1.computeIfAbsent(entry.getKey(), k -> new ArrayList<>());
value.addAll(entry.getValue());
}
);
}
);
}
Про устаревшие API уже выше сказали. На мой взгляд, код, использующий устаревшее API, нельзя назвать красивым.
Со стримами и лямбдами многое иначе выглядит.
void buildDictionaryWithMap(CharSequence text) {
Function<Character, int[]> mkelem = x -> new int[1];
text.chars()
// .parallel()
.map(Character::toLowerCase)
.map(c -> c == 'ё' ? 'е' : c)
.filter(c -> c >= 'а' && c <= 'я')
.collect(
HashMap<Character, int[]>::new,
(r, c) -> r.computeIfAbsent((char) c, mkelem)[0]++,
(a, b) -> b.forEach((k, v) -> a.computeIfAbsent(k, mkelem)[0] += v[0])
)
.entrySet().stream().sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.printf("%c %d%n", e.getKey(), e.getValue()[0]));
}
void buildDictionaryWithoutMap(CharSequence text) {
var freqs = text.chars()
// .parallel()
.map(Character::toLowerCase)
.map(c -> c == 'ё' ? 'е' : c)
.filter(c -> c >= 'а' && c <= 'я')
.collect(
() -> new int['я' - 'а' + 1],
(r, c) -> r[c - 'а']++,
(a, b) -> { for (int i = 0; i < a.length; i++) a[i] += b[i]; }
);
for (int i = 0; i < freqs.length; i++) {
if (freqs[i] > 0) {
System.out.printf("%c %d%n", (char) (i + 'а'), freqs[i]);
}
}
}
<K, V> Map<V, Collection<K>> inverse(Map<? extends K, ? extends V> map) {
Function<V, Collection<K>> mkelem = x -> new ArrayList<>();
return map.entrySet().stream()
// .parallel()
.collect(
HashMap::new,
(r, e) -> r.computeIfAbsent(e.getValue(), mkelem).add(e.getKey()),
(a,b) -> b.forEach((k,v) -> a.computeIfAbsent(k,mkelem).addAll(v))
);
}
Я, например, вижу поехавшие отступы.
Значит, это проблема редактура. Наверно, стоит о таких вещах сообщать через ctrl+enter. Так я хотя бы узнаю о чём именно Вы говорите.
Runnable — функциональный интерфейс. Начиная, с 8-й явы его не надо анонимным классом реализовывать. Вместо этого надо лямбду создавать.
В иммутабельном классе Vector поля надо было сделать final.
Я знаю. Но не стоит забывать, что в курсе есть все-таки некоторая последовательность. Если Вы даёте эту задачу студентам, когда они про лямбды и final же в курсе (первая задача на ООП?), то Вы можете это требовать и в решении тоже их показать.
Стримы вообще не представлены никак. А это уже 5 лет как ключевое API.
Да тут много чего не представлено ещё. Всё будет!
За остальные замечания спасибо!
Реализация некоторых итераторов нарушает контракт метода next() — при отсутствии следующего элемента тот должен выбрасывать NoSuchElementException (а не возвращать null / бросать исключение другого типа).
Проверка в строках от а до я не цепляет букву ё.
В 6.1 моё мнение — лучше было использовать паттерн комманда в чистом виде, чтобы не плодить action. Все равно список команд маленький и енама хватило бы какого. Ну и да, имеются вопросы производительности, есть лучше варианты. (Они все деревья, neerc.ifmo.ru/wiki/index.php?title=Персистентный_массив пример).
Но все равно, подборка задач хорошая, думаю пригодится)
Если про добавить задач, то мне кажется, стоит уделить внимание работе с файлами и сетью чуть раньше чем на итоговом занятии. Это бы дало возможность тексты для частотных анализов вводить из файла. И, возможно, базы данных, особенно dao было бы полезно.
достаточно проверять до квадратного корня делители
Про корень это не единственное, что можно здесь оптимизировать. Ещё есть Решето Эратосфена. Но обычно я даю на первом занятии самое простое решение. Вы можете делать иначе, конечно же.
7.8) В методах, принимающих в качестве параметра дженерики, желательно указывать не конкретный тип, а границы (wildcard bounds), чтобы можно было сделать вот так:
<K, V> Map<V, Collection<K>> inverse(Map<? extends K, ? extends V> map) { ... }
Map<String, Integer> map1 = ...
Map<Number, Collection<Object>> map2 = inverse(map1);
P. S. Сам так очень редко делаю, но для методических материалов все должно быть четко.
Нужно еще несколько алгоритмов на стеки и очереди, обход, поиск зацикливанияб сортировки например Merge, Insertion, Selection. Еще полезно дать например обход графа.
А для особо крутых ребят сделать сереализацию POJO модельки в json через рефлекцию.
Возможно сделать так же парсинг страниц html через Jsoup например.
В качестве простой задачи на графы можно предложить реализовать головоломку "волк-коза-капуста". Ее также можно усложнить, если спрашивать у пользователя правила несовместимости условных волков и коз.
Box extends Shape - имхо, такое ООП попахивает плохо
Для вывода на экран массива можно использовать
System.out.println(Arrays.toString(array));
вместо цикла.
Спасибо за такой набор задач, как раз недавно начал изучать джаву и хотел себе список задач для практики.
Практические задачи по Java — для курсов и прочих занятий