JavaRush /Java Blogu /Random-AZ /Java tərtibatçısı üçün müsahibələrdən alınan sualların və...

Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili. 13-cü hissə

Qrupda dərc edilmişdir
Salam!
Bir məqsədə doğru hərəkət, ilk növbədə, hərəkətdir.
Ona görə də nəyəsə nail olmaq istədiyinizi düşünmək kifayət deyil. Siz bir şey etməlisiniz - hətta ən kiçik addımlar da - ancaq onları hər gün edin və yalnız bu şəkildə son məqsədə çatacaqsınız. Java tərtibatçıları olmaq üçün burada olduğunuz üçün hər gün Java haqqında biliklərinizi dərinləşdirmək üçün ən azı minimal addım atmalısınız. Bugünkü Java addımı üçün sizə tərtibatçılar üçün ən populyar müsahibə suallarının təhlilinin yeni hissəsi ilə tanış olmağı təklif edirəm. Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-1 hissəBu gün biz kiçik mütəxəssislər üçün sualların praktiki hissəsindən keçəcəyik. Müsahibə zamanı praktiki tapşırıq qeyri-adi deyil. Belə bir vəziyyətdə itməmək, soyuqqanlı davranmağa çalışmaq və optimal həlli, hətta bir neçəsini təklif etmək vacibdir. Mən də məsləhət görərdim ki, problemi həll edərkən səssiz qalmasın, düşüncə qatarınızı şərh edib həllini yazın və ya yazdıqdan sonra nə etdiyinizi və niyə etdiyinizi sözlə izah edin. Bu, sizi səssiz qərardan daha çox müsahibə sevdirəcək. Beləliklə, başlayaq!

111. Mövzular arasında məlumat mübadiləsi necə aparılır?

Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-2 hissəMövzular arasında məlumat mübadiləsi üçün bir çox fərqli yanaşma və vasitələrdən istifadə edə bilərsiniz: məsələn, atom dəyişənlərindən, sinxronlaşdırılmış kolleksiyalardan və semafordan istifadə edin. Ancaq bu problemi həll etmək üçün Exchanger ilə bir nümunə verəcəyəm . Mübadilə cihazı , ümumi sinxronizasiya nöqtəsi yaratmaqla bir cüt ip arasında elementlərin mübadiləsini asanlaşdıran paralel paketdən sinxronizasiya sinfidir . Onun istifadəsi iki mövzu arasında məlumat mübadiləsini asanlaşdırır. İşləmə üsulu olduqca sadədir: onun exchange() metodunu çağırmaq üçün iki ayrı mövzu gözləyir . Onların arasında mübadilə nöqtəsi kimi bir şey yaranır: birinci sap öz obyektini qoyur və əvəzində digərinin obyektini alır, ikincisi isə öz növbəsində birincinin obyektini qəbul edərək özününkini qoyur. Yəni, birinci başlıq exchange() metodundan istifadə edir və başqa bir ip eyni obyektdə exchange() metodunu çağırana və onlar arasında məlumat mübadiləsinə qədər boş qalır . Nümunə olaraq, Thread sinfinin aşağıdakı tətbiqini nəzərdən keçirin :
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();
     }
   }
 }
}
Mövzu konstruktorunda biz String tipli obyektləri qəbul edən Exchanger obyektini müəyyənləşdiririk və işə salındıqda (çalışma metodunda ) biz eyni Exchanger- də bu metoddan istifadə edən başqa mövzu ilə mesaj mübadiləsi üçün onun exchange() funksiyasından istifadə edirik . Gəlin onu əsas işə salaq :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
Konsol göstərəcək:
Birinci mövzu mesajı aldı: İkinci mövzudan mesaj İkinci mövzu mesajı aldı: Birinci mövzudan mesaj İkinci mövzu mesajı aldı: İkinci mövzudan mesaj Birinci mövzu mesajı aldı: Birinci mövzudan mesaj İkinci mövzu mesajı aldı: Birinci mövzudan mesaj Birinci mövzu mesajı aldı: İkinci mövzudan mesaj... .
Bu, mövzular arasında məlumat mübadiləsinin uğurlu olması deməkdir.

112. Thread sinfi ilə Runnable interfeysi arasında fərq nədir?

Qeyd edəcəyim ilk şey Thread bir sinifdir, Runnable interfeysdir, bu çox açıq bir fərqdir =D Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-3-cü hissəMən onu da deyim ki, Thread Runnable (kompozisiya) istifadə edir . Yəni iki yolumuz var:
  1. Thread- dən miras alın , run metodunu ləğv edin, sonra bu obyekti yaradın və başlığı start() metodu ilə başlayın .

  2. Müəyyən bir sinifdə Runnable tətbiq edin , onun run() metodunu tətbiq edin və sonra Runnable interfeysinin bu obyekt tətbiqini konstruktoruna təyin edərək Thread obyekti yaradın . Yaxşı, sonunda start() metodundan istifadə edərək Thread obyektini işə salın .

Nəyə üstünlük verilir? Gəlin bir az düşünək:
  • Runnable interfeysini həyata keçirdiyiniz zaman mövzunun davranışını dəyişmirsiniz. Əslində siz sadəcə ipə işləmək üçün bir şey verirsiniz. Bu da bizim kompozisiyamızdır ki, bu da öz növbəsində yaxşı yanaşma hesab olunur.

  • Runnable tətbiqi sinifinizə daha çox çeviklik verir. Əgər siz Thread- dan miras alırsınızsa , onda yerinə yetirdiyiniz hərəkət həmişə mövzuda olacaq. Ancaq Runnable tətbiq etsəniz, o, sadəcə bir iplik olmamalıdır. Axı, ya onu bir mövzuda işlədə və ya hansısa icraçı xidmətinə ötürə bilərsiniz. Yaxşı, və ya sadəcə bir yivli tətbiqdə tapşırıq kimi bir yerə keçin.

  • Runnable- dan istifadə etmək sizə tapşırığın icrasını mövzu nəzarəti məntiqindən məntiqi olaraq ayırmağa imkan verir.

  • Java-da yalnız bir miras mümkündür, ona görə də yalnız bir sinif genişləndirilə bilər. Eyni zamanda, genişləndirilə bilən interfeyslərin sayı qeyri-məhduddur (yaxşı, olduqca qeyri-məhdud deyil, lakin 65535 , lakin bu həddi heç vaxt vura bilməyəcəksiniz).

Yaxşı, hansının istifadəyə üstünlük verdiyi sizə qərar verməkdir ^^

113. T1, T2 və T3 ipləri var. Onları ardıcıl olaraq necə həyata keçirmək olar?Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-4-cü hissə

Ağla gələn ilk və ən sadə şey join() metodundan istifadə etməkdir . O, cari ipin (metod adlanan) icrasını, metodun çağırıldığı mövzu icrasını bitirənə qədər dayandırır. Gəlin öz mövzu tətbiqimizi yaradaq:
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 + " - закончил свою работу");
 }
}
Gəlin join() funksiyasından istifadə edərək üç belə mövzunu bir-bir başlayaq :
CustomThread t1 = new CustomThread("Первый поток");
t1.start();
t1.join();
CustomThread t2 = new CustomThread("Второй поток");
t2.start();
t2.join();
CustomThread t3 = new CustomThread("Третий поток");
t3.start();
t3.join();
Konsol çıxışı:
Birinci ip - işə başladı Birinci ip - işini bitirdi İkinci ip - işə başladı İkinci ip - işini bitirdi Üçüncü ip - işə başladı Üçüncü ip - işini bitirdi
Bu o deməkdir ki, biz tapşırığı yerinə yetirmişik. Sonra biz birbaşa Junior səviyyəsində praktiki tapşırıqlara keçirik .

Praktik tapşırıqlar

114. Matris diaqonal cəmi (Leetcode problemi)

Şərt: Əsas diaqonalın bütün elementlərinin və əsas diaqonalın bir hissəsi olmayan əlavə diaqonalın bütün elementlərinin cəmini hesablayın. Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-5-ci hissə1. Formanın matrisi ilə: mat = [[1,2,3], [4,5,6], [7,8,9]] Çıxış - 25 olmalıdır 2. Matrislə - mat = [[1,1 ,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Çıxış - 8 3 olmalıdır. a matrix - mat = [[ 5]] Nəticə belə olmalıdır - 5 Oxumağa fasilə verin və qərarınızı həyata keçirin. Mənim həll yolum belə olacaq:
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;
}
Hər şey massivdən bir keçidlə baş verir, bu müddət ərzində hesabat üçün iki indeksimiz var: i - massivin sətirlərinin və əsas diaqonalın sütunlarının hesabatı üçün, j - əlavə diaqonalın sütunlarının hesabatı üçün. Əsas diaqonalın və əlavənin xanası üst-üstə düşürsə, məbləğ hesablanarkən dəyərlərdən biri nəzərə alınmır. Şərtdən matrislərdən istifadə edərək yoxlayaq:
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));
Konsol çıxışı:
25 8 5

115. Sıfırları köçürün (Leetcode problemi)

Şərt: Tam ədədlər massivində sıfırdan fərqli elementlərin nisbi sırasını qoruyaraq bütün 0-ları sonuna qədər köçürün. 1. Massivlə: [0,1,0,3,12] Çıxış belə olmalıdır: [1,3,12,0,0] 2. Massivlə: [0] Çıxış belə olmalıdır: [0] Pause və qərarımı yazın... Qərarım:
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;// заполняем последние элементы массива нулями согласно счётчику нулей
 }
}
İmtahan:
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));
Konsol çıxışı:
[1, 2, 12, 9, 0, 0] [0]

116. Verilmiş Siyahı <String> adları. Hər addan ilk hərfi çıxarın və çeşidlənmiş siyahını çevirin

1. İlk ağla gələn kolleksiyalar üçün bir çox köməkçi metodları ehtiva edən Collections sinifinin metodlarıdır :
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. Həmçinin, əgər Java 8 və daha yüksək versiyalardan istifadə etsək, sadəcə olaraq, həlli axınlar vasitəsilə göstərməliyik:
public static List<String> processTheList(List<String> nameList) {
 return nameList.stream()
     .map(x -> x.substring(1))
     .sorted().collect(Collectors.toList());
}
Seçilmiş həlldən asılı olmayaraq, yoxlama aşağıdakı kimi ola bilər:
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));
Konsol çıxışı:
[avid, eter, gor, mitriy, nna, ob, ohn]

117. Massivi çevirin

Həll 1 Yenə ağla gələn ilk şey Collections köməkçi köməkçi sinifinin metodlarından istifadə etməkdir . Lakin bizdə massiv olduğu üçün əvvəlcə onu kolleksiyaya (siyahıya) çevirməliyik:
public static Integer[] reverse(Integer[] arr) {
 List<Integer> list = Arrays.asList(arr);
 Collections.reverse(list);
 return list.toArray(arr);
}
Həll 2 Sual massivlə bağlı olduğundan, məncə, hazır funksionallıqdan istifadə etmədən həlli göstərmək lazımdır, belə desək, klassiklərə görə:
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;
}
İmtahan:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Konsol çıxışı:
[9, 8, 7, 6, 5, 4, 3, 2, 1]

118. Simin palindrom olub olmadığını yoxlayın

Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-6 hissəHəll 1 StringBuilder-i dərhal xatırlamağa dəyər : o, adi String ilə müqayisədə daha çevik və müxtəlif üsullarla zəngindir . Bizi xüsusilə tərs üsul maraqlandırır :
public static boolean isPalindrome(String string) {
 string = string.toLowerCase(); //приводит всю строку к нижнему регистру
 StringBuilder builder = new StringBuilder();
 builder.append(string);
 builder.reverse(); // перевочиваем строку методом Builder-а
 return (builder.toString()).equals(string);
}
Həll yolu: Növbəti yanaşma qutudan çıxarılan “boşluqlardan” istifadə etmədən olacaq. Sətin arxasındakı simvolları ön tərəfdəki müvafiq simvollarla müqayisə edirik:
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;
}
Və hər iki yanaşmanı yoxlayırıq:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Konsol çıxışı:
doğru

119. Sadə çeşidləmə alqoritmini yazın (Bubble, Selection və ya Shuttle). Onu necə təkmilləşdirmək olar?

Tətbiq üçün sadə bir alqoritm olaraq, mən seçim çeşidlənməsini seçdim - Seçim 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 итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
Təkmilləşdirilmiş versiya belə görünür:
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;
}
Yaxşı, indi çeşidləmənin həqiqətən yaxşılaşdığını yoxlamalıyıq. Performansı müqayisə edək:
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 + "%");
Hər iki növ eyni dövrədə başladı, çünki əgər ayrı-ayrı döngələr olsaydı, yuxarıdakı koddan çeşidləmə ikinci yerdə qoyulduğundan daha pis nəticə göstərərdi. Bu, proqramın "istiləşməsi" və sonra bir az daha sürətli işləməsi ilə əlaqədardır. Amma mövzudan bir az kənara çıxıram. Konsolda bu yoxlamanın beş qaçışından sonra performansda artım gördüm: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.09198070574370612 , bu mənim üçün. olduqca yaxşı nəticə. Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-7 hissə

120. int tipli literalın bayt tipli hərfi ilə tərtib edilməsi alqoritmini (hərəkətlər ardıcıllığını) yazın. Yaddaşla nə baş verdiyini izah edin

  1. bayt dəyəri int-ə çevrilir. Bunun üçün 1 bayt yaddaş ayrılmayacaq, lakin bütün int dəyərləri kimi - 4, əgər bu dəyər hələ int yığınında deyilsə. Əgər varsa, ona bir keçid sadəcə alınacaq.

  2. İki int dəyəri əlavə olunacaq və üçüncü əldə ediləcək. Bunun üçün yeni yaddaş bölməsi ayrılacaq - 4 bayt (yaxud int yığınından mövcud dəyərə istinad alınacaq).

    Bu halda, iki intin yaddaşı hələ də tutulacaq və onların dəyərləri müvafiq olaraq int yığınında saxlanacaqdır.

Əslində siyahımızda olan Junior səviyyəli sualların bitdiyi yer budur. Növbəti məqalədən başlayaraq biz Orta səviyyəli məsələləri başa düşəcəyik. Qeyd etmək istərdim ki, orta səviyyəli suallar həm də giriş səviyyəli tərtibatçılara - Junior-a aktiv şəkildə verilir. Odur ki, bizi izləyin. Yaxşı, bu gün üçün hamısı: görüşənədək!Java tərtibatçısı üçün müsahibələrdən alınan sualların və cavabların təhlili.  13-8 hissə
Seriyadakı digər materiallar:
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION