JavaRush /Java Blog /Random-TL /Pagsusuri ng mga tanong at sagot mula sa mga panayam para...

Pagsusuri ng mga tanong at sagot mula sa mga panayam para sa developer ng Java. Bahagi 13

Nai-publish sa grupo
Kamusta!
Ang paggalaw patungo sa isang layunin ay, una sa lahat, kilusan.
Samakatuwid, hindi sapat na isipin lamang na nais mong makamit ang isang bagay. Kailangan mong gawin ang isang bagay - kahit na ang pinakamaliit na hakbang - ngunit gawin ang mga ito araw-araw, at sa paraang ito lamang makakamit mo ang pangwakas na layunin. At dahil narito ka para maging mga developer ng Java, kailangan mong gumawa ng kahit kaunting hakbang tungo sa pagpapalalim ng iyong kaalaman sa Java araw-araw. Para sa hakbang ng Java ngayon, iminumungkahi kong pamilyar ka sa bagong bahagi ng pagsusuri ng mga pinakasikat na tanong sa panayam para sa mga developer. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 1Ngayon ay dadaan tayo sa praktikal na bahagi ng mga tanong para sa mga Junior specialist. Ang isang praktikal na gawain sa isang pakikipanayam ay hindi karaniwan. Mahalaga na huwag mawala sa ganoong sitwasyon, subukang panatilihing malamig ang ulo at mag-alok ng pinakamainam na solusyon, o kahit na marami. Inirerekumenda ko rin na huwag manatiling tahimik kapag nilulutas ang isang problema, ngunit magkomento sa iyong tren ng pag-iisip at isulat ang solusyon, o pagkatapos magsulat, ipaliwanag sa mga salita kung ano ang iyong ginawa at bakit. Mas mapapamahal ka nito sa tagapanayam kaysa sa isang tahimik na desisyon. Kaya simulan na natin!

111. Paano makipagpalitan ng data sa pagitan ng mga thread?

Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 2Upang makipagpalitan ng data sa pagitan ng mga thread, maaari kang gumamit ng maraming iba't ibang mga diskarte at paraan: halimbawa, gumamit ng mga atomic variable, naka-synchronize na mga koleksyon, at isang semaphore. Ngunit upang malutas ang problemang ito, magbibigay ako ng isang halimbawa sa Exchanger . Ang Exchanger ay isang klase ng pag-synchronize mula sa kasabay na pakete na nagpapadali sa pagpapalitan ng mga elemento sa pagitan ng isang pares ng mga thread sa pamamagitan ng paglikha ng isang karaniwang punto ng pag-synchronize. Pinapasimple ng paggamit nito ang pagpapalitan ng data sa pagitan ng dalawang thread. Ang paraan ng paggana nito ay medyo simple: naghihintay ito ng dalawang magkahiwalay na thread na tawagan ang exchange() method nito . Isang bagay na tulad ng isang exchange point ay nilikha sa pagitan nila: ang unang thread ay naglalagay ng bagay nito at tumatanggap ng bagay ng isa bilang kapalit, at ang huli, naman, ay tumatanggap ng bagay ng una at inilalagay ang kanyang sarili. Iyon ay, ang unang thread ay gumagamit ng exchange() na paraan at idle hanggang ang isa pang thread ay tumawag sa exchange() na paraan sa parehong bagay at ang data ay ipinagpapalit sa pagitan nila. Bilang halimbawa, isaalang-alang ang sumusunod na pagpapatupad ng klase ng Thread :
public class CustomThread extends Thread {
 private String threadName;
 private String message;
 private Exchanger<String> exchanger;

 public CustomThread(String threadName, Exchanger<String> exchanger) {
   this.threadName = threadName;
   this.exchanger = exchanger;
 }

 public void setMessage(final String message) {
   this.message = message;
 }

 @Override
 public void run() {
   while (true) {
     try {
       message = exchanger.exchange(message);
       System.out.println(threadName + " поток получил сообщение: " + message);
       Thread.sleep(1000);
     } catch (Exception e) {
       e.printStackTrace();
     }
   }
 }
}
Sa thread constructor, tinutukoy namin ang isang Exchanger object na tumatanggap ng mga object ng uri ng String , at sa startup (sa run method ) ginagamit namin ang exchange() nito para makipagpalitan ng mensahe sa isa pang thread na gumagamit ng paraang ito sa parehong Exchanger . Patakbuhin natin ito sa pangunahing :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
Ipapakita ng console ang:
Первый поток получил сообщение: Сообщение второго потока Второй поток получил сообщение: Сообщение первого потока Второй поток получил сообщение: Сообщение второго потока Первый поток получил сообщение: Сообщение первого потока Второй поток получил сообщение: Сообщение первого потока Первый поток получил сообщение: Сообщение второго потока ...
Это значит, что обмен данными между потоками проходит успешно.

112. В чем заключается отличие класса Thread от интерфейса Runnable?

Первое, что отмечу, Thread — это класс, Runnable — интерфейс, что весьма очевидное отличие =D Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 3Также скажу, что Thread использует Runnable (композиция). То есть, у нас есть два пути:
  1. Наследоваться от Thread, переопределить метод run, после чего создать данный an object и запустить поток через метод start().

  2. Реализовать Runnable в определенном классе, реализовать его метод run(), после чего создать an object Thread, задав ему в конструктор этот an object-реализацию интерфейса Runnable. Ну и в конце запустить an object Thread с помощью метода start().

What же предпочтительнее? Давайте немного поразмыслим:
  • при реализации интерфейса Runnable вы не изменяете поведение потока. По сути вы просто даете потоку что-то запустить. А это у нас композиция, что в свою очередь считается хорошим подходом.

  • реализация Runnable даёт больше гибкости вашему классу. Если вы наследуетесь от Thread, то действие, которое вы выполняете, всегда будет в потоке. Но если вы реализуете Runnable, это не обязательно будет просто поток. Ведь вы можете How запустить его в потоке, так и передать его Howой-либо службе-исполнителю. Ну or просто передать его куда-то How задачу в однопоточном приложении.

  • использование Runnable позволяет логически отделить выполнение задачи от логики управления потоками.

  • в Java возможно только одиночное наследование, поэтому можно расширить только один класс. В то же время количество расширяемых интерфейсов неограниченно (ну не совсем неограниченное, а 65535, но вряд ли вы когда-то упретесь в этот лимит).

Ну а что именно предпочтительнее использовать, решать уже вам ^^

113. Есть потоки Т1, Т2 и Т3. Как реализовать их последовательное выполнение?Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 4

Самое первое и самое простое, что приходит на ум — это использование метода join(). Он приостанавливает выполнение текущего (вызвавшего данный метод) потока до тех пор, пока поток, на котором был вызван метод, не закончит свое выполнение. Создадим свою реализацию потока:
public class CustomThread extends Thread {
private String threadName;

 public CustomThread(final String  threadName){
   this.threadName = threadName;
 }

 @Override
 public void run() {
   System.out.println(threadName + " - начал свою работу");
   try {
     // происходит некая логика
     Thread.sleep(1000);
   } catch (InterruptedException e) {
     e.printStackTrace();
   }

   System.out.println(threadName + " - закончил свою работу");
 }
}
Запустим три таких потока поочередно, используя join():
CustomThread t1 = new CustomThread("Первый поток");
t1.start();
t1.join();
CustomThread t2 = new CustomThread("Второй поток");
t2.start();
t2.join();
CustomThread t3 = new CustomThread("Третий поток");
t3.start();
t3.join();
Вывод в консоли:
Первый поток - начал свою работу Первый поток - закончил свою работу Второй поток - начал свою работу Второй поток - закончил свою работу Третий поток - начал свою работу Третий поток - закончил свою работу
Это значит, что мы справorсь с нашей задачей. Далее переходим непосредственно к практическим задачам уровня Junior.

Практические задания

114. Matrix Diagonal Sum (задача с Leetcode)

Условие: Подсчитайте сумму всех элементов на основной диагонали и всех элементов на дополнительной диагонали, которые не являются частью основной диагонали. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 51. При матрице вида : mat = [[1,2,3], [4,5,6], [7,8,9]] Вывод должен быть — 25 2. При матрице - mat = [[1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Вывод должен быть — 8 3. При матрице - mat = [[5]] Вывод должен быть — 5 Сделайте паузу в прочтении и реализуйте своё решение. Мое же решение будет следующим:
public static int countDiagonalSum(int[][] matrix) {
 int sum = 0;
 for (int i = 0, j = matrix.length - 1; i < matrix.length; i++, j--) {
   sum += matrix[i][i];
   if (j != i) {
     sum += matrix[i][j];
   }
 }
 return sum;
}
Всё происходит с помощью одного прохода по массиву, во время которого у нас есть два индекса для отчёта: i — для отчёта строк массива и колонок основной диагонали, j — для отчёта колонок дополнительной диагонали. Если же ячейка основной диагонали и дополнительной совпадают, то одно из значений игнорируется при подсчете суммы. Проверим, используя матрицы из условия:
int[][] arr1 = {
   {1, 2, 3},
   {4, 5, 6},
   {7, 8, 9}};
System.out.println(countDiagonalSum(arr1));

int[][] arr2 = {
   {1, 1, 1, 1},
   {1, 1, 1, 1},
   {1, 1, 1, 1},
   {1, 1, 1, 1}};
System.out.println(countDiagonalSum(arr2));

int[][] arr3 = {{5}};
System.out.println(countDiagonalSum(arr3));
Вывод в консоли:
25 8 5

115. Move Zeroes (задача с Leetcode)

Условие: В целочисленном массиве переместите все 0 в конец, сохраняя относительный порядок ненулевых элементов. 1. При массиве: [0,1,0,3,12] Вывод должен быть: [1,3,12,0,0] 2. При массиве: [0] Вывод должен быть: [0] Сделайте паузу и напишите свое решение ... Моё решение:
public static void moveZeroes(int[] nums) {
 int counterWithoutNulls = 0;
 int counterWithNulls = 0;
 int length = nums.length;
 while (counterWithNulls < length) {
   if (nums[counterWithNulls] == 0) {// находим нулевые элементы и увеличиваем счётчик
     counterWithNulls++;
   } else { // сдвигаем элементы на количество найденных нулевых элементов слева
     nums[counterWithoutNulls++] = nums[counterWithNulls++];
   }
 }
 while (counterWithoutNulls < length) {
   nums[counterWithoutNulls++] = 0;// заполняем последние элементы массива нулями согласно счётчику нулей
 }
}
Проверка:
int[] arr1 = {1, 2, 0, 0, 12, 9};
moveZeroes(arr1);
System.out.println(Arrays.toString(arr1));

int[] arr2 = {0};
moveZeroes(arr2);
System.out.println(Arrays.toString(arr2));
Вывод в консоль:
[1, 2, 12, 9, 0, 0] [0]

116. Given List <String> names. Удалите первую букву из каждого имени и поверните отсортированный список

1. Первое, что приходит в голову, это методы класса Collections, хранящий в себе множество вспомогательных методов для коллекций:
public static List<String> processTheList(List<String> nameList) {
 for (int i = 0; i < nameList.size(); i++) {
   nameList.set(i, nameList.get(i).substring(1));
 }
 Collections.sort(nameList);
 return nameList;
}
2. Также если мы используем Java версии 8 и выше мы просто обязаны показать решение через стримы:
public static List<String> processTheList(List<String> nameList) {
 return nameList.stream()
     .map(x -> x.substring(1))
     .sorted().collect(Collectors.toList());
}
Независимо от выбранного решения, проверка может быть следующая:
List<String> nameList = new ArrayList();
nameList.add("John");
nameList.add("Bob");
nameList.add("Anna");
nameList.add("Dmitriy");
nameList.add("Peter");
nameList.add("David");
nameList.add("Igor");

System.out.println(processTheList(nameList));
Вывод в консоли:
[avid, eter, gor, mitriy, nna, ob, ohn]

117. Переверните массив

Решение 1 Опять же, первое, что приходит в голову — использовать методы вспомогательного, утorтного класса Collections. Но так How у нас массив, сперва нужно преобразовать его в коллекцию (список):
public static Integer[] reverse(Integer[] arr) {
 List<Integer> list = Arrays.asList(arr);
 Collections.reverse(list);
 return list.toArray(arr);
}
Решение 2 Так How вопрос был про массив, думаю, необходимо показать решение и без использования готового функционала из коробки, а так сказать, по классике:
public static Integer[] reverse(Integer[] arr) {
 for (int i = 0; i < arr.length / 2; i++) {
   int temp = arr[i];
   arr[i] = arr[arr.length - 1 - i];
   arr[arr.length - 1 - i] = temp;
 }
 return arr;
}
Проверка:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Вывод в консоли:
[9, 8, 7, 6, 5, 4, 3, 2, 1]

118. Проверить, является ли строка палиндромом

Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 6Решение 1 Стоит сразу вспомнить о StringBuilder: он более гибкий и насыщенный различными методами по сравнению с обычным String. Нас особенно интересует метод reverse:
public static boolean isPalindrome(String string) {
 string = string.toLowerCase(); //приводит всю строку к нижнему регистру
 StringBuilder builder = new StringBuilder();
 builder.append(string);
 builder.reverse(); // перевочиваем строку методом Builder-а
 return (builder.toString()).equals(string);
}
Решение: Следующий подход будет без использования “лазеек” из коробки. Сравниваем символы из задней части строки с соответствующими символами из передней:
public static boolean isPalindrome(String string) {
  string = string.toLowerCase();
 int length = string.length();
 int fromBeginning = 0;
 int fromEnd = length - 1;
 while (fromEnd > fromBeginning) {
   char forwardChar = string.charAt(fromBeginning++);
   char backwardChar = string.charAt(fromEnd--);
   if (forwardChar != backwardChar)
     return false;
 }
 return true;
}
И проверяем оба подхода:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Вывод в консоли:
true

119. Написать простой алгоритм сортировки (Bubble, Selection or Shuttle). Как его можно улучшить?

В качестве просто алгоритма для реализации я выбрал сортировку выбором — Selection Sort:
public static void selectionSorting(int[] arr) {
 for (int i = 0; i < arr.length - 1; i++) {
   int min = i;
   for (int j = i + 1; j < arr.length; j++) {
     if (arr[j] < arr[min]) {
       min = j; // выбираем минимальный элемент в текущем числовом отрезке
     }
   }
   int temp = arr[min]; // меняем местами минимальный элемент с элементом под индексом i
   arr[min] = arr[i]; // так How отрезок постоянно уменьшается
   arr[i] = temp; // и выпадающие из него числа будут минимальными в текущем отрезке
 } // и How итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
Улучшенный вариант будет выглядеть следующим образом:
public static void improvedSelectionSorting(int[] arr) {
 for (int i = 0, j = arr.length - 1; i < j; i++, j--) { // рассматриваемый отрезок с каждой итерацией
   // будет уменьшаться с ДВУХ сторон по одному элементу
   int min = arr[i];
   int max = arr[i];
   int minIndex = i;
   int maxIndex = i;
   for (int n = i; n <= j; n++) { // выбираем min и max на текущем отрезке
     if (arr[n] > max) {
       max = arr[n];
       maxIndex = n;
     } else if (arr[n] < min) {
       min = arr[n];
       minIndex = n;
     }
   }
   // меняем найденный минимальный элемент с позиции с индексом min на позицию с индексом i
   swap(arr, i, minIndex);

   if (arr[minIndex] == max) {// срабатывает, если элемент max оказался смещен предыдущей перестановкой -
     swap(arr, j, minIndex); // на старое место min, поэтому с позиции с индексом min смещаем его на позицию j
   } else {
     swap(arr, j, maxIndex); // простое обмен местами элементов с индексами max и j
   }
 }
}

static int[] swap(int[] arr, int i, int j) {
 int temp = arr[i];
 arr[i] = arr[j];
 arr[j] = temp;
 return arr;
}
Ну а теперь нам нужно убедиться, правда ли sorting улучшилась. Давайте сравним производительность:
long firstDifference = 0;
long secondDifference = 0;
long primaryTime;
int countOfApplying = 10000;
for (int i = 0; i < countOfApplying; i++) {
 int[] arr1 = {234, 33, 123, 4, 5342, 76, 3, 65,
     3, 5, 35, 75, 255, 4, 46, 48, 4658, 44, 22,
     678, 324, 66, 151, 268, 433, 76, 372, 45, 13,
     9484, 499959, 567, 774, 473, 3, 32, 865, 67, 43,
     63, 332, 24, 1};
 primaryTime = System.nanoTime();
 selectionSorting(arr1);
 firstDifference += System.nanoTime() - primaryTime;

 int[] arr2 = {234, 33, 123, 4, 5342, 76, 3, 65,
     3, 5, 35, 75, 255, 4, 46, 48, 4658, 44, 22,
     678, 324, 66, 151, 268, 433, 76, 372, 45, 13,
     9484, 499959, 567, 774, 473, 3, 32, 865, 67, 43,
     63, 332, 24, 1};
 primaryTime = System.nanoTime();
 improvedSelectionSorting(arr2);
 secondDifference += System.nanoTime() - primaryTime;
}

System.out.println(((double) firstDifference / (double) secondDifference - 1) * 100 + "%");
Обе сортировки запустorсь в одном и том же цикле, т.к. если бы были отдельные циклы, sorting из в codeе выше показывала бы худший результат, нежели если её поставить второй. Это связано с тем, что программа How бы “разогревается” и дальше работает немного быстрее. Но я немного отошёл от темы. После пяти запусков данной проверки в консоли я увидел увеличение производительности на: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% Как по мне, это довольно-таки хороший результат. Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 7

120. Напишите алгоритм (последовательность действий) составления литерала типа int с литералом типа byte. Объясните, что происходит с памятью

  1. byte meaning приводится к int. Для него будет выделен не 1 byte памяти, а How и для всех int значений — 4, если этого значения ещё нет в int стеке. Если же есть — просто будет получена link на него.

  2. Два int значения будут сложены и получится третье. Под него выделится новый участок памяти — 4 byteа (либо будет получена link из int стека на существующее meaning).

    При этом память двух int всё ещё будет занята, и их значения будут храниться в int стеке соответственно.

Собственно, на этом и заканчиваются вопросы уровня Junior из нашего списка. Начиная со следующей статьи мы будем разбираться в вопросах уровня Middle. Отмечу, что вопросы Middle-уровня активно задают и разработчикам начального уровня — Junior. Так что следите за обновлениями. Ну а на сегодня всё: до встречи!Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 13 - 8
Другие материалы серии:
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION