JavaRush /وبلاگ جاوا /Random-FA /تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا...

تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا. قسمت 13

در گروه منتشر شد
سلام!
حرکت به سوی هدف اول از همه حرکت است.
بنابراین، تنها فکر کردن به اینکه می خواهید به چیزی برسید کافی نیست. شما باید کاری را انجام دهید - حتی کوچکترین قدم ها - اما آنها را هر روز انجام دهید و تنها از این طریق به هدف نهایی خواهید رسید. و از آنجایی که شما اینجا هستید تا توسعه دهندگان جاوا شوید، باید هر روز حداقل یک قدم برای تعمیق دانش خود در مورد جاوا بردارید. برای مرحله جاوا امروز، پیشنهاد می کنم با بخش جدید تجزیه و تحلیل محبوب ترین سوالات مصاحبه برای توسعه دهندگان آشنا شوید. تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 1امروز بخش عملی سوالات متخصصان جوان را مرور خواهیم کرد. یک کار عملی در مصاحبه غیر معمول نیست. مهم است که در چنین شرایطی گم نشوید، سعی کنید خونسردی خود را حفظ کنید و راه حل بهینه یا حتی چندین مورد را ارائه دهید. همچنین توصیه می‌کنم هنگام حل مشکل سکوت نکنید، بلکه درباره رشته فکرتان نظر بدهید و راه‌حل را بنویسید یا بعد از نوشتن، با کلمات توضیح دهید که چه کردید و چرا. این شما را بسیار بیشتر از یک تصمیم خاموش برای مصاحبه کننده محبوب می کند. پس بیایید شروع کنیم!

111. چگونه داده ها را بین رشته ها تبادل کنیم؟

تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 2برای تبادل داده‌ها بین رشته‌ها، می‌توانید از روش‌ها و ابزارهای مختلفی استفاده کنید: به عنوان مثال، از متغیرهای اتمی، مجموعه‌های همگام‌سازی شده و یک سمافور استفاده کنید. اما برای حل این مشکل با Exchanger مثال می زنم . مبدل یک کلاس همگام سازی از بسته همزمان است که با ایجاد یک نقطه همگام سازی مشترک، تبادل عناصر بین یک جفت رشته را تسهیل می کند. استفاده از آن تبادل داده بین دو رشته را ساده می کند. روش کار بسیار ساده است: منتظر دو رشته مجزا می ماند تا متد () exchange خود را فراخوانی کند . چیزی شبیه نقطه مبادله بین آنها ایجاد می شود: نخ اول شیء خود را می گذارد و در عوض شیء دیگری را دریافت می کند و دومی نیز به نوبه خود شیء اولی را دریافت می کند و مال خود را می گذارد. یعنی اولین رشته از متد () exchange استفاده می کند و تا زمانی که رشته دیگری متد exchange() را روی همان شیء فراخوانی کند و داده ها بین آنها رد و بدل شود بیکار است. به عنوان مثال، پیاده سازی زیر از کلاس 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();
     }
   }
 }
}
در سازنده thread، یک شی Exchanger تعریف می‌کنیم که اشیایی از نوع String را می‌پذیرد ، و در هنگام راه‌اندازی (در روش run ) از exchange() آن برای تبادل پیام با رشته‌ای دیگر که از این متد در همان Exchanger استفاده می‌کند استفاده می‌کنیم . بیایید آن را به صورت اصلی اجرا کنیم :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
کنسول نمایش خواهد داد:
تاپیک اول پیام را دریافت کرد: پیام از تاپیک دوم تاپیک دوم پیام را دریافت کرد: پیام از تاپیک اول تاپیک دوم پیام را دریافت کرد: پیام از تاپیک دوم اولین موضوع پیام را دریافت کرد: پیام از تاپیک اول دومین موضوع تاپیک پیغام دریافت کرد: پیام از تاپیک اول تاپیک اول پیام دریافت کرد: پیام از تاپیک دوم... .
این بدان معنی است که تبادل داده بین رشته ها موفقیت آمیز است.

112. تفاوت کلاس Thread و رابط Runnable چیست؟

اولین چیزی که به آن اشاره می کنم این است که Thread یک کلاس است، Runnable یک رابط است، که یک تفاوت بسیار آشکار است =D تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 3همچنین می گویم که Thread از Runnable (ترکیب) استفاده می کند . یعنی دو راه داریم:
  1. از Thread ارث بری کنید ، متد run را لغو کنید، سپس این شی را ایجاد کنید و رشته را از طریق متد start() شروع کنید .

  2. Runnable را در یک کلاس خاص پیاده‌سازی کنید ، متد run() آن را پیاده‌سازی کنید، و سپس یک شی Thread ایجاد کنید و این شیء پیاده‌سازی رابط Runnable را به سازنده آن اختصاص دهید . خوب، در پایان، شی Thread را با استفاده از متد start() اجرا کنید .

چه چیزی ارجح است؟ بیایید کمی فکر کنیم:
  • وقتی رابط Runnable را پیاده سازی می کنید ، رفتار رشته را تغییر نمی دهید. در اصل شما فقط به موضوع چیزی برای اجرا می دهید. و این ترکیب ماست که به نوبه خود رویکرد خوبی محسوب می شود.

  • اجرای Runnable انعطاف پذیری بیشتری به کلاس شما می دهد. اگر از Thread ارث ببرید ، عملی که انجام می‌دهید همیشه در thread خواهد بود. اما اگر Runnable را پیاده سازی کنید ، لازم نیست که فقط یک رشته باشد. پس از همه، شما می توانید آن را در یک رشته اجرا کنید یا آن را به برخی از سرویس های اجرایی ارسال کنید. خوب، یا فقط آن را به عنوان یک کار در یک برنامه تک رشته ای منتقل کنید.

  • استفاده از Runnable به شما این امکان را می دهد که به طور منطقی اجرای وظیفه را از منطق کنترل رشته جدا کنید.

  • در جاوا، تنها وراثت واحد امکان پذیر است، بنابراین فقط یک کلاس را می توان گسترش داد. در عین حال، تعداد اینترفیس‌های قابل ارتقا نامحدود است (خب، نه کاملاً نامحدود، اما 65535 ، اما بعید است که هرگز به این حد نرسید).

خوب، اینکه دقیقاً چه چیزی ترجیح داده می شود، به شما بستگی دارد که تصمیم بگیرید ^^

113. نخ های T1، T2 و T3 وجود دارد. چگونه آنها را به صورت متوالی پیاده سازی کنیم؟تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 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();
خروجی کنسول:
تاپیک اول - کارش را شروع کرد تاپیک اول - کارش را تمام کرد تاپیک دوم - کارش را شروع کرد تاپیک دوم - کارش را تمام کرد تاپیک سوم - کارش را شروع کرد تاپیک سوم - کارش را تمام کرد
این به این معنی است که ما وظیفه خود را انجام داده ایم. بعد، مستقیماً به سمت کارهای عملی در سطح جونیور حرکت می کنیم .

وظایف عملی

114. جمع مورب ماتریس (مسئله Leetcode)

شرط: مجموع تمام عناصر روی قطر اصلی و تمام عناصر روی قطر اضافی که جزء قطر اصلی نیستند را محاسبه کنید. تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 51. با یک ماتریس به شکل: mat = [[1،2،3]، [4،5،6]، [7،8،9]] خروجی باید - 25 باشد. با یک ماتریس - mat = [[1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] خروجی باید - 8 3 باشد. a matrix - 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. حرکت صفرها (چالش 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. فهرست داده شده نام <String>. اولین حرف را از هر نام حذف کنید و لیست مرتب شده را بچرخانید

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. همچنین، اگر از جاوا نسخه 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 دوباره، اولین چیزی که به ذهن می رسد استفاده از روش های کلاس ابزار کمکی Collections است . اما از آنجایی که یک آرایه داریم، ابتدا باید آن را به یک مجموعه (لیست) تبدیل کنیم:
public static Integer[] reverse(Integer[] arr) {
 List<Integer> list = Arrays.asList(arr);
 Collections.reverse(list);
 return list.toArray(arr);
}
راه حل 2 از آنجایی که سوال در مورد یک آرایه بود، فکر می کنم لازم است که راه حل را بدون استفاده از قابلیت های آماده خارج از جعبه نشان دهیم و به اصطلاح، طبق کلاسیک ها:
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. بررسی کنید که آیا یک رشته پالیندروم است یا خیر

تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 6راه حل 1 ارزش آن را دارد که بلافاصله StringBuilder را به خاطر بسپارید: در مقایسه با String معمولی از نظر روش های مختلف انعطاف پذیرتر و غنی تر است . ما به ویژه به روش معکوس علاقه مندیم :
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);
خروجی کنسول:
درست است، واقعی

119. یک الگوریتم مرتب سازی ساده (حباب، انتخاب یا شاتل) بنویسید. چگونه می توانید آن را بهبود بخشید؟

به عنوان یک الگوریتم ساده برای پیاده سازی، من مرتب سازی انتخاب را انتخاب کردم - 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;
}
خوب، اکنون باید مطمئن شویم که آیا مرتب سازی واقعاً بهبود یافته است یا خیر. بیایید عملکرد را با هم مقایسه کنیم:
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 + "%");
هر دو نوع در یک چرخه شروع شدند، زیرا اگر حلقه‌های جداگانه وجود داشت، مرتب‌سازی از کد بالا نتایج بدتری نسبت به زمانی که در رتبه دوم قرار می‌گرفت، نشان می‌داد. این به این دلیل است که برنامه "گرم می شود" و سپس کمی سریعتر کار می کند. اما من کمی از موضوع خارج می شوم. پس از پنج بار اجرای این بررسی در کنسول، شاهد افزایش عملکرد به میزان: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.0919807057235892% برای این است. نتیجه خیلی خوب تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 7

120. یک الگوریتم (توالی اعمال) برای نوشتن یک کلمه از نوع int با یک بایت از نوع تحت اللفظی بنویسید. توضیح دهید که چه اتفاقی برای حافظه می افتد

  1. مقدار بایت به int تبدیل می شود. 1 بایت حافظه برای آن اختصاص داده نمی شود، اما مانند همه مقادیر int - 4، اگر این مقدار هنوز در پشته int نباشد. اگر وجود داشته باشد، یک لینک به آن به سادگی دریافت می شود.

  2. دو مقدار int اضافه می شود و سومی به دست می آید. یک بخش حافظه جدید برای آن اختصاص داده می شود - 4 بایت (یا یک مرجع از پشته int به مقدار موجود دریافت می شود).

    در این حالت، حافظه دو int همچنان اشغال می شود و مقادیر آنها به ترتیب در پشته int ذخیره می شود.

در واقع، اینجا جایی است که سوالات سطح Junior از لیست ما به پایان می رسد. با شروع از مقاله بعدی، مسائل سطح متوسط ​​را درک خواهیم کرد. می خواهم توجه داشته باشم که سوالات سطح میانی نیز به طور فعال از توسعه دهندگان سطح ابتدایی - Junior پرسیده می شود. پس با ما همراه باشید. خوب، این همه برای امروز است: می بینمت!تجزیه و تحلیل پرسش و پاسخ از مصاحبه برای توسعه دهنده جاوا.  قسمت 13 - 8
سایر مواد این سری:
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION