JavaRush /Blog Java /Random-MS /Analisis soalan dan jawapan daripada temu bual untuk pemb...

Analisis soalan dan jawapan daripada temu bual untuk pembangun Java. Bahagian 13

Diterbitkan dalam kumpulan
hello!
Pergerakan ke arah matlamat adalah, pertama sekali, pergerakan.
Oleh itu, tidak cukup dengan hanya berfikir bahawa anda ingin mencapai sesuatu. Anda perlu melakukan sesuatu - walaupun langkah terkecil - tetapi lakukannya setiap hari, dan hanya dengan cara ini anda akan mencapai matlamat akhir. Dan kerana anda berada di sini untuk menjadi pembangun Java, anda perlu mengambil sekurang-kurangnya langkah minimum ke arah mendalami pengetahuan Java anda setiap hari. Untuk langkah Java hari ini, saya cadangkan anda membiasakan diri dengan bahagian baharu analisis soalan temu bual yang paling popular untuk pembangun. Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 1Hari ini kita akan meneliti bahagian praktikal soalan untuk pakar Junior. Tugas praktikal semasa temu duga bukanlah perkara biasa. Adalah penting untuk tidak tersesat dalam situasi sedemikian, cuba untuk mengekalkan kepala yang tenang dan menawarkan penyelesaian yang optimum, atau bahkan beberapa. Saya juga mengesyorkan agar tidak berdiam diri semasa menyelesaikan masalah, tetapi mengulas tentang pemikiran anda dan menulis penyelesaiannya, atau selepas menulis, terangkan dengan perkataan apa yang anda lakukan dan mengapa. Ini akan menjadikan anda disayangi oleh penemuduga lebih daripada keputusan senyap. Jadi mari kita mulakan!

111. Bagaimana untuk menukar data antara benang?

Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 2Untuk menukar data antara utas, anda boleh menggunakan pelbagai pendekatan dan cara: contohnya, gunakan pembolehubah atom, koleksi disegerakkan dan semafor. Tetapi untuk menyelesaikan masalah ini, saya akan memberikan contoh dengan Exchanger . Penukar ialah kelas penyegerakan daripada pakej serentak yang memudahkan pertukaran elemen antara sepasang benang dengan mencipta titik penyegerakan biasa. Penggunaannya memudahkan pertukaran data antara dua utas. Cara ia berfungsi agak mudah: ia menunggu dua utas berasingan untuk memanggil kaedah exchange() nya . Sesuatu seperti titik pertukaran dicipta di antara mereka: benang pertama meletakkan objeknya dan menerima objek yang lain sebagai balasan, dan yang terakhir, seterusnya, menerima objek yang pertama dan meletakkan objeknya sendiri. Iaitu, thread pertama menggunakan kaedah exchange() dan melahu sehingga thread lain memanggil kaedah exchange() pada objek yang sama dan data ditukar antara mereka. Sebagai contoh, pertimbangkan pelaksanaan kelas Thread berikut :
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();
     }
   }
 }
}
Dalam pembina thread, kami mentakrifkan objek Exchanger yang menerima objek jenis String , dan semasa permulaan (dalam kaedah run ) kami menggunakan exchange() untuk menukar mesej dengan thread lain yang menggunakan kaedah ini dalam Exchanger yang sama . Mari jalankannya dalam utama :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
Konsol akan memaparkan:
Utas pertama menerima mesej: Mesej utas kedua Utas kedua menerima mesej: Mesej utas pertama Utas kedua menerima mesej: Mesej utas kedua Utas pertama menerima mesej: Mesej utas pertama Utas kedua utas menerima mesej: Mesej utas pertama Urut pertama menerima mesej: Mesej utas kedua... .
Ini bermakna pertukaran data antara benang berjaya.

112. Apakah perbezaan antara kelas Thread dan antara muka Runnable?

Perkara pertama yang saya akan perhatikan ialah Thread ialah kelas, Runnable ialah antara muka, yang merupakan perbezaan yang sangat jelas =D Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 3Saya juga akan mengatakan bahawa Thread menggunakan Runnable (komposisi). Iaitu, kita mempunyai dua cara:
  1. Warisi daripada Thread , ganti kaedah run, kemudian buat objek ini dan mulakan thread melalui kaedah start() .

  2. Laksanakan Runnable dalam kelas tertentu, laksanakan kaedah run() nya , dan kemudian buat objek Thread , berikan pelaksanaan objek ini antara muka Runnable kepada pembinanya . Nah, pada akhirnya, lancarkan objek Thread menggunakan kaedah start() .

Apa yang lebih disukai? Mari kita fikirkan sedikit:
  • Apabila anda melaksanakan antara muka Runnable , anda tidak mengubah tingkah laku benang. Pada asasnya anda hanya memberikan benang sesuatu untuk dijalankan. Dan ini adalah komposisi kami, yang seterusnya dianggap sebagai pendekatan yang baik.

  • melaksanakan Runnable memberikan lebih fleksibiliti kepada kelas anda. Jika anda mewarisi daripada Thread , maka tindakan yang anda lakukan akan sentiasa berada pada thread. Tetapi jika anda melaksanakan Runnable, ia tidak semestinya hanya benang. Lagipun, anda boleh sama ada menjalankannya dalam benang atau menyerahkannya kepada beberapa perkhidmatan pelaksana. Baiklah, atau hanya serahkannya ke suatu tempat sebagai tugas dalam aplikasi berbenang tunggal.

  • Menggunakan Runnable membolehkan anda memisahkan pelaksanaan tugas secara logik daripada logik kawalan benang.

  • Di Jawa, hanya warisan tunggal yang mungkin, jadi hanya satu kelas boleh dilanjutkan. Pada masa yang sama, bilangan antara muka yang boleh dikembangkan adalah tidak terhad (baik, bukan tidak terhad, tetapi 65535 , tetapi anda tidak mungkin mencapai had ini).

Nah, apa sebenarnya yang lebih baik untuk digunakan terpulang kepada anda untuk membuat keputusan ^^

113. Terdapat benang T1, T2 dan T3. Bagaimana untuk melaksanakannya secara berurutan?Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 4

Perkara pertama dan paling mudah yang terlintas di fikiran ialah menggunakan kaedah join() . Ia menangguhkan pelaksanaan utas semasa (yang dipanggil kaedah) sehingga utas yang mana kaedah itu dipanggil selesai melaksanakan. Mari kita buat pelaksanaan thread kita sendiri:
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 + " - закончил свою работу");
 }
}
Mari kita mulakan tiga utas sedemikian satu demi satu menggunakan 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();
Output konsol:
Benang pertama - memulakan kerja Benang pertama - selesai kerja Benang kedua - memulakan kerja Benang kedua - selesai kerja Benang ketiga - memulakan kerja Benang ketiga - selesai kerjanya
Ini bermakna kami telah menyelesaikan tugas kami. Seterusnya, kami bergerak terus ke tugas praktikal di peringkat Junior .

Tugasan praktikal

114. Jumlah Diagonal Matriks (masalah Leetcode)

Keadaan: Kira jumlah semua unsur pada pepenjuru utama dan semua unsur pada pepenjuru tambahan yang bukan sebahagian daripada pepenjuru utama. Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 51. Dengan matriks dalam bentuk: mat = [[1,2,3], [4,5,6], [7,8,9]] Output hendaklah - 25 2. Dengan matriks - mat = [[1,1 ,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Output hendaklah - 8 3. Dengan a matriks - mat = [[ 5]] Kesimpulannya hendaklah - 5 Jeda bacaan dan laksanakan keputusan anda. Penyelesaian saya adalah seperti berikut:
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;
}
Semuanya berlaku dengan satu laluan melalui tatasusunan, di mana kita mempunyai dua indeks untuk laporan: i - untuk melaporkan baris tatasusunan dan lajur pepenjuru utama, j - untuk melaporkan lajur pepenjuru tambahan. Jika sel pepenjuru utama dan yang tambahan bertepatan, maka salah satu nilai diabaikan semasa mengira jumlahnya. Mari kita semak menggunakan matriks dari keadaan:
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));
Output konsol:
25 8 5

115. Move Zeroes (cabaran Leetcode)

Keadaan: Dalam tatasusunan integer, gerakkan semua 0 ke penghujung, mengekalkan susunan relatif unsur bukan sifar. 1. Dengan tatasusunan: [0,1,0,3,12] Output hendaklah: [1,3,12,0,0] 2. Dengan tatasusunan: [0] Output hendaklah: [0] Jeda dan tulis keputusan saya... Keputusan saya:
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;// заполняем последние элементы массива нулями согласно счётчику нулей
 }
}
Peperiksaan:
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));
Output konsol:
[1, 2, 12, 9, 0, 0] [0]

116. Nama Senarai <String> yang diberikan. Keluarkan huruf pertama daripada setiap nama dan putar senarai yang diisih

1. Perkara pertama yang terlintas di fikiran ialah kaedah kelas Koleksi , yang mengandungi banyak kaedah tambahan untuk koleksi:
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. Selain itu, jika kami menggunakan Java versi 8 dan lebih tinggi, kami hanya perlu menunjukkan penyelesaian melalui strim:
public static List<String> processTheList(List<String> nameList) {
 return nameList.stream()
     .map(x -> x.substring(1))
     .sorted().collect(Collectors.toList());
}
Terlepas dari penyelesaian yang dipilih, semakan mungkin seperti berikut:
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));
Output konsol:
[avid, eter, gor, mitriy, nna, ob, ohn]

117. Balikkan tatasusunan

Penyelesaian 1 Sekali lagi, perkara pertama yang terlintas di fikiran ialah menggunakan kaedah Koleksi kelas utiliti tambahan . Tetapi kerana kita mempunyai tatasusunan, kita perlu menukarnya terlebih dahulu menjadi koleksi (senarai):
public static Integer[] reverse(Integer[] arr) {
 List<Integer> list = Arrays.asList(arr);
 Collections.reverse(list);
 return list.toArray(arr);
}
Penyelesaian 2 Memandangkan soalan itu adalah mengenai tatasusunan, saya fikir ia perlu untuk menunjukkan penyelesaian tanpa menggunakan fungsi siap sedia di luar kotak, dan boleh dikatakan, mengikut klasik:
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;
}
Peperiksaan:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Output konsol:
[9, 8, 7, 6, 5, 4, 3, 2, 1]

118. Periksa sama ada rentetan ialah palindrom

Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 6Penyelesaian 1 Perlu diingati dengan segera StringBuilder : ia lebih fleksibel dan kaya dengan pelbagai kaedah berbanding String biasa . Kami amat berminat dengan kaedah terbalik :
public static boolean isPalindrome(String string) {
 string = string.toLowerCase(); //приводит всю строку к нижнему регистру
 StringBuilder builder = new StringBuilder();
 builder.append(string);
 builder.reverse(); // перевочиваем строку методом Builder-а
 return (builder.toString()).equals(string);
}
Penyelesaian: Pendekatan seterusnya adalah tanpa menggunakan "celah" di luar kotak. Kami membandingkan aksara dari belakang rentetan dengan aksara yang sepadan dari hadapan:
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;
}
Dan kami menyemak kedua-dua pendekatan:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Output konsol:
benar

119. Tulis algoritma pengisihan mudah (Bubble, Selection atau Shuttle). Bagaimana ia boleh diperbaiki?

Sebagai algoritma mudah untuk pelaksanaan, saya memilih jenis pilihan - Isih Pemilihan:
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 итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
Versi yang dipertingkatkan akan kelihatan seperti ini:
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;
}
Nah, sekarang kita perlu memastikan sama ada pengisihan telah benar-benar bertambah baik. Mari bandingkan prestasi:
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 + "%");
Kedua-dua jenis bermula dalam kitaran yang sama, kerana jika terdapat gelung yang berasingan, pengisihan daripada dalam kod di atas akan menunjukkan hasil yang lebih teruk berbanding jika ia diletakkan di tempat kedua. Ini disebabkan oleh fakta bahawa program "memanaskan badan" dan kemudian berfungsi sedikit lebih cepat. Tetapi saya akan keluar sedikit daripada topik. Selepas lima larian semakan ini dalam konsol, saya melihat peningkatan dalam prestasi sebanyak: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.0919807057435616% Oleh kerana ini adalah 2.091980705743566661% hasil yang cukup baik. Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 7

120. Tulis algoritma (urutan tindakan) untuk mengarang literal jenis int dengan literal jenis bait. Terangkan apa yang berlaku kepada ingatan

  1. nilai bait ditukar kepada int. Tidak 1 bait ingatan akan diperuntukkan untuknya, tetapi seperti semua nilai int - 4, jika nilai ini belum ada pada timbunan int. Jika ada, pautan kepadanya hanya akan diterima.

  2. Dua nilai int akan ditambah dan yang ketiga akan diperolehi. Bahagian memori baharu akan diperuntukkan untuknya - 4 bait (atau rujukan akan diterima daripada timbunan int kepada nilai sedia ada).

    Dalam kes ini, memori dua int masih akan diduduki, dan nilainya masing-masing akan disimpan pada timbunan int.

Sebenarnya, di sinilah soalan peringkat Junior dari senarai kami berakhir. Bermula dari artikel seterusnya, kita akan memahami isu peringkat pertengahan. Saya ingin ambil perhatian bahawa soalan peringkat pertengahan juga ditanya secara aktif kepada pembangun peringkat permulaan - Junior. Jadi nantikan. Nah, itu sahaja untuk hari ini: jumpa anda!Analisis soalan dan jawapan daripada temu bual untuk pembangun Java.  Bahagian 13 - 8
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION