Комментарии 47
Верным ответом на такую задачу будет запрос:
Зачем JOIN, если d.id IS NULL? ?? Так себе верность
Тоже не понял зачем тут JOIN
не привязанных к отделам
достаточно запроса к таблице employee: ... Where department_id is null and ...
Вероятно тут попытка определить существует ли в таблице отделов отдел с идентификатором, который храниться в поле department_id таблицы сотрудников.
А в этом поле может быть или NULL, или 0, или ещё какое-то значение, которые показывает что у сотрудника нет отдела.
Поэтому самое надёжное чекнуть таблицу отделов.
Критикуя - предлагай. Как еще найти не привязанных к департаментам? e.id not in (select id from department)? Сомнительное решение..
Проверка
e.id not in (select id from department)
не просто "сомнительна" она в принципе не корректна, так какselect id from department
выберет идентификаторы департаментов, тогда какe.id
это идентификаторы сотрудников и они не связаны между собой и в том числе могут и пересекатьсяВ комментариях уже предложили решение:
e.department_id is null
. Правда оно рассчитано на то что структура БД корректна иe.department_id
имеетFK
на таблицуdepartment
Да я ошибся не e.id, а e.
department_id
.А если department_id = 0?
Если в БД существует нормальный FK
, то в e.department_id
не будет ерунды, в том числе нуля и других значений которых нет в таблице department
.
Если же FK
нет (что как раз и позволяет хранить в e.department_id
значения навроде 0
) тогда JOIN
будет верятно всё же более адекватным вариантом чем проверки на уровне записей.
Я считаю что изначально нужно рассчитывать на FK
, если другое не сказано явно и уже после уточнения вносить правки в запрос на основе новых сведений, а додумывать что-то дело не благодарное - иначе можно дойти до того что "не привязанных к отделам" можно интепретировать как "работник может быть приязан к нескольким отделам", а значит в department_id
будет лежать список id
отделов и это нужно учитывать.
P.S.
И это тоже обсуждалось.
Полностью согласен! :)
На верный ответ это точно не тянет :)
Мне одному показалось, что мужик на главной картинке, одет как юнит из strongholda?:)
Но вторая задача же по объёму больше всех остальных вместе взятых.
В этом случае её следует вынести в отдельное design-interview (ну или в отдельную дизайн-сессию интервью).
блин, вторую задачу я так наизусть без IDE и доки по спрингу решать реально не просто. Есть ряд вещей которые вот так на поверхности памяти не лежат
Предложу такое решение 1й задачи.
import java.util.List;
import java.util.stream.IntStream;
public class Main {
private static final int MIN = 0;
private static final int MAX = 1000;
private static final int GOOD_DIV = 3;
private static final int BAD_DIV = 5;
private static final int DIGIT_SUM_CONDITION = 10;
public static void main(String[] args) {
List<Integer> numList = IntStream.range(MIN, MAX).boxed()
.filter(i -> (i % GOOD_DIV == 0 && i % BAD_DIV != 0 && checkSumDigits(i)))
.toList();
System.out.println(numList);
}
private static boolean checkSumDigits(int num) {
int val = 0;
while (num > 0) {
val += num % 10;
num = num / 10;
if (val >= DIGIT_SUM_CONDITION) {
return false;
}
}
return true;
}
}
Вот за это я и не люблю стримы, люди на ровном месте для решения простейшей задачи изобретают супер сложные конструкции без всякой причины. Проще надо быть
import static java.lang.System.out;
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
if (i % 3 == 0 && i % 5 != 0 && (i / 100 + (i / 10) % 10 + i % 10) < 10) {
out.println(i);
}
}
out.print(1000);
}
}
Результат такой же, но гораздо проще для понимания и поддержки, потому что программист оперирует 6-ю словами всего (for, int, i, if, out, print)
вместо 16-ти (List, Integer, numList, IntStream, range, MIN, MAX, boxed, filter, i, GOOD_DIV, BAD_DIV, toList, System, out, print)
Данное решение
в дальнейшем нужно будет поддерживать
Sonar
всё равно возмутится хардкоду значений в коде - и нужно будет выносить константыразвёрнутое вычисление суммы цифр это быстро, но работает только для
1000
- если максимум увеличится в размере всего на пару цифр, то развёрнутая формула вычисления суммы станет совсем не читаемой1000
не делится на3
и должно отсутствовать в выводе, а оно присутствует
Это, конечно, не повод использовать стримы, но и пропускать такой код через ревью я бы не стал.
Вы придумали несуществующие требования
Это решение ненадо поддерживать, это просто тестовое задание на собеседовании. Его никто не будет ревьюить, его выкинут сразу же после окончания собеседования
Никто не будет запускать Sonar на этом коде
Решение работает для конкретного заданного случая и заданного диапазона, естественно не является универсальным
Да, с 1000 я облажался :-(. Тут согласен.
Вы придумали несуществующие требования
Выдержка из постановки к задаче 1:
Работодателю здесь важно понять, как соискатель владеет основами синтаксиса языка и может ли писать код сразу чисто.
С помощью задач такого типа мы проверяем уровень соискателя: как много он писал код сам, а также косвенно — сталкивался ли с «грязным» кодом и рефакторил ли его. По такому заданию можно косвенно увидеть, как быстро и «чисто» (понятно и поддерживаемо) специалист будет писать собственный код.
Из этого следует что код должен быть достаточно "опрятным" и "поддерживаемым".
Да, Sonar
тут не будет прогоняться и код, действительно, будет "выброшен", но именно по этому коду идёт оценка того что кандидат будет "творить" в дальнейшем - можно писать "лишь бы работало", но это явно не покажет кандидата с лучшей стороны.
Да и вы сами пишете:
Результат такой же, но гораздо проще для понимания и поддержки
, хотя проверка суммы цифр явно таковой не является даже в текущем варианте (суммирующем всего 3 позиции) и только ухудшится при изменении условий (что часто происходит с таким кодом на собесах - типа "а что будет если диапазон будет не до 1000
, а до 20000
?" - см. упоминание рефакторинга).
Решение работает для конкретного заданного случая и заданного диапазона, естественно не является универсальным
Вычисление суммы цифр легко превращается в функцию, которая
работает для любых значений
повышает читаемость и поддерживаемость
не снижает производительности
повышает тестируемость и модульность
Во 2-м примере метод returnNumber вообще не вызывается
Во втором примере вы забыли код дописать
public class FizzBuz1 {
public static void main(String[] args) {
for (int i = 3; i <= 1000; i += 3) {
if (i % 5 != 0 && check(i)) {
System.out.println(i);
}
}
}
private static boolean check(int i) {
int r = 0;
while (i > 0 && r < 10) {
r += i % 10;
i = i / 10;
}
return r < 10;
}
}
один товарищ изложил свою версию, чем джун отличается от мидла. первый напишет fizzbuzz через if-ы или case. а опытный кодер знает, что проект может меняться. и вместо фиксированного количества множителей мы напишем рекурсивную функцию, которая принимает от нуля аргументов и далее. поэтому шаг в цикле точно будет 1. пишу по памяти, сам не советчик здесь ) Кажется, смысл в том, чтоб программа была легко изменяемой через классы и интерфейсы.
Чет какие-то сложные решения в комментариях. Предлагаю следующее:
IntStream.range(0, 1000)
.filter(
((IntPredicate) e -> (e % 3) == 0)
.and(e -> (e % 5) != 0)
.and( e -> {
int sd =
Integer.valueOf(e)
.toString()
.chars()
.map(e1 -> Character.getNumericValue(e1))
.reduce(Integer::sum)
.orElse(0);
return sd < 10;
})
)
.forEach(System.out::println);
Предложу свой вариант первой задачи (если не создавать переменные по условиям задачи):
public class Task01 {
/*
* написать код, который выводит числа от 0 до 1000, которые делятся на 3, но не
* делятся на 5, и сумма цифр в которых меньше десяти
*/
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
if (i % 3 == 0 & i % 5 != 0 & sumFigure(i) < 10) {
System.out.println(i);
}
}
}
static int sumFigure(int num) {
return num % 10 + num / 10 + num / 100 + num / 1000;
}
}
public class main {
public static void main(String args[]) {
for (int i=0; i<=1000; i++)
{
int a=i;
int b=0;
while(a > 0)
{
a=a%10;
a=a/10;
b=b+a;
}
/* System.out.println(z);*/
if ((b < 10) & (i % 3 == 0) & (i % 5 !=0))
{
System.out.println(i);
}
}
}
}
Решение первой задачи
package org.example.test2;
public class Test2 {
public static void main(String[] args) {
int j;
int sum = 0;
for (int i = 0; i < 1000; i++) {
if ((i % 3 == 0) && (i % 5 != 0)) {
j = i;
while (j > 0) {
sum += j % 10;
j /= 10;
}
if (sum < 10) {
System.out.println(i);
}
sum = 0;
}
}
}
}
Чтобы экономить деления, цикл +=3 и вложенный в цикл счётчик 1..5.
Или даже лучше цикл от 3 с шагом 15 и внутри вывод (на проверку) чисел i+0, +3, +6, +9, +12.
Ну или от 1 и проверка чисел i+2, +5,…
Вариант с i+3
уже предложили.
А по остальным предложениям: напишите код, а не мысли вслух - давайте рассмотрим.
public static void main(String[] args) {
int[] list = {3, 6, 9, 12};
for (int i = 0; i < 1000; i += 15) {
for (int j = 0; j < 4; j++) {
int curr = i + list[j];
if (curr / 100 + curr / 10 % 10 + curr % 10 < 10) {
System.out.println(curr);
}
}
}
}
И даже проверка «curr <= 1000» лишняя, так как 10nn/100 =10 и отсечётся здесь
Я накидал бенчмарк по вариантам предложенным в комментариях (плюс мои) и получил вот такие данные:
| Benchmark | Mode | Cnt | Score ± Error | Units | vs `naive` | vs `simple` |
|-----------|-------|-----|----------------------|-------|------------|-------------|
| naive | thrpt | 10 | 53348,090 ± 4429,824 | ops/s | ------- | -27.110% |
| simple | thrpt | 10 | 73190,139 ± 5990,700 | ops/s | +37.19% | ------- |
| pin2t | thrpt | 10 | 77951,880 ± 6964,085 | ops/s | +46.12% | +06.51% |
| igolikov | thrpt | 10 | 79527,875 ± 5324,465 | ops/s | +49.07% | +08.66% |
| rombell | thrpt | 10 | 83608,345 ± 3828,489 | ops/s | +56.72% | +14.23% |
Что интерпретировать можно так:
ваш, @rombell, вариант самый быстрый, но корректно работает строго в диапазоне до
1000
(так как проверяет только 3 знака) и при сумме цифр именно10
потом идёт вариант от @igolikov, за счёт отсутствия проверки делимости на
3
и внесения проверки лимита суммы цифр в цикл подсчёта этой суммыпотом идёт вариант от @pin2t, за счёт проверки суммы только трёх знаков (из-за чего и работает корректно только на диапазоне до
1000
)и потом мой вариант
Плюс 14%
это весьма хороший показатель.
А вот с учётом поддерживаемости, расширяемости и тестируемости побеждает вариант от @igolikov - он и работает на любых диапазонах и делает это довольно быстро (ваш быстрее всего на 5%
) и доступен для понимания.
Кстати, есть ещё небольшие улучшения, например, добавить проверку <=900 и сэкономить несколько относительно дорогих операций за счёт одного сравнения. И вырезать промежуток 811..899. И 721..799. Даст ещё некоторый прирост. Можно даже предварительно насчитать границы, пользуясь нашими знаниями и десятичной системе счисления.
Решая конкретную задачу, разумно использовать знания об этой задаче.
Мой вариант решал конкретную задачу.
Исходная постановка звучит примерно так: Решите задачу X
с помощью опрятного и поддерживаемого кода. По результирующему коду будет оцениваться то как будет писаться код для боевых задач.
Решая конкретную задачу, разумно использовать знания об этой задаче.
Но ваша реализация уже выходит за границы KISS
и сильно уходит в сторону преждевременной оптимизации: требований по написанию максимально эффективного кода не было, в отличии от пожеланий написания поддерживаемого кода.
Где границы поддерживаемости? Обобщить задачу — это одно, а полностью изменить — совсем другое. Мой код вполне прозрачен и поддерживаем, изменение предельной суммы цифр либо границ делается в одной точке. Изменение пропускаемых делителей — замена шага цикла либо массива.
Что до оптимизации, так преждевременной оптимизацией было бы деление на промежутки, в предлагаемом решении нет никаких сложностей, только использование знаний о конкретной задаче.
Ещё раз обращаю внимание, все решения используют десятичную систему, не пытаясь обобщить на остальные системы счисления (в задаче, кстати, б хтом не было ни слова). Не-использование знаний о задаче — неправильно. Потом у нас для простейших действий получаются мегабайтный код. Зато фреймворк на фреймворке.
В общем, конечно, свой код лучше судить года через полтора, но пока что он мне кажется поддерживаемым не хуже прочих, и уж точно проще в анализе, чем циклы вычисления суммы цифр.
На этом предлагаю и разойтись ))
IntStream.range(1, 1000)
.boxed()
.filter(i -> i % 3 == 0 && i % 5 != 0 && String.valueOf(i).length() > 1
&& String.valueOf(i).chars().mapToObj(c -> Integer.valueOf(""+(char)c)).reduce(0, Integer::sum) < 10)
.collect(Collectors.toList());
поганое задание. Не на знание алгоритмики или логики, а на зубрение методов работы со Strings. Не ходите к таким на собеседования.
А причём тут "методы работы со
String
"? Вычисление "суммы цифр" числа через преобразование числа в строку и получение потока символов используется только у вас и у @DenisPantushev тогда как есть более "алгоритмический" вариант без, так сказать "зубрения".Из-за проверки
String.valueOf(i).length() > 1
ваш код упускает часть корректных значений.Вместо
String.valueOf(i).chars().mapToObj(c -> Integer.valueOf(""+(char)c)).reduce(0, Integer::sum)
можно использовать гораздо более короткий, понятный и эффективный вариант:String.valueOf(i).chars().map(Character::getNumericValue).sum()
.А зачем
IntStream
боксируется вStream<Integer>
?
Всё это у вас бы спросили на собесе.
for (int i = 0; i <= 1000; i++) {
String s = i + "";
char[] charArr = s.toCharArray();
int sum = 0;
for (char c: charArr) {
sum += Integer.parseInt(String.valueOf(c));
}
if (sum < 10 && i % 3 == 0 && i % 5 != 0) {
System.out.println(i);
}
}
Пять задач, которые приходится решать при трудоустройстве начинающим Java-разработчикам в 2022 году