JavaRush /Java блогу /Random-KY /Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун...
Константин
Деңгээл

Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи. 13-бөлүк

Группада жарыяланган
Салам!
Максатка карай кыймыл – бул эң оболу кыймыл.
Ошондуктан, бир нерсеге жетүүнү каалайт деп эле ойлоо жетишсиз. Сиз бир нерсе кылышыңыз керек - эң кичинекей кадамдарды да - бирок аларды күн сайын жасаңыз, ошондо гана сиз акыркы максатка жетесиз. Жана сиз Java иштеп чыгуучулары болуу үчүн бул жерде болгондуктан, күн сайын Java боюнча бorмиңизди тереңдетүү үчүн жок дегенде минималдуу кадам ташташыңыз керек. Бүгүнкү Java кадамы үчүн мен сизге иштеп чыгуучулар үчүн эң популярдуу интервью суроолорунун анализинин жаңы бөлүгү менен таанышууну сунуштайм. Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  13-1-бөлүкБүгүн биз кенже адистер үчүн суроолордун практикалык бөлүгүн карап чыгабыз. Интервьюдагы практикалык тапшырма сейрек эмес. Мындай кырдаалда адашып калбоо, салкын карманууга жана оптималдуу чечимди сунуштоого аракет кылуу керек, ал тургай бир нече. Мен ошондой эле көйгөйдү чечүүдө унчукпай, өзүңүздүн ой жүгүртүүңүзгө комментарий берип, чечимди жазууну же жазгандан кийин эмне кылганыңызды жана эмне үчүн сөз менен түшүндүрүүнү сунуштайт элем. Бул сизди интервью берүүчүгө унчукпай чечим чыгарууга караганда көбүрөөк жакшы көрөт. Ошентип, баштайлы!

111. Жиптердин ортосунда маалымат алмашуу кандай болот?

Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  13-2-бөлүкЖиптер арасында маалымат алмашуу үчүн, сиз көптөгөн ар кандай ыкмаларды жана каражаттарды колдоно аласыз: мисалы, атомдук өзгөрмөлөрдү, синхрондоштурулган коллекцияларды жана семафорду колдонуңуз. Бирок бул маселени чечүү үчүн, мен бир мисал келтирем Exchanger . Алмаштыргыч - бул жалпы синхрондоштуруу чекитинин түзүү аркылуу бир жуп жиптин ортосундагы элементтердин алмашуусун жеңилдеткен бир мезгилдештирүү классы . Аны колдонуу эки жиптин ортосундагы маалымат алмашууну жеңилдетет. Анын иштөө ыкмасы абдан жөнөкөй: анын exchange() ыкмасын чакырышы үчүн ал эки башка жипти күтөт . Алардын ортосунда алмашуу чекити сыяктуу бир нерсе түзүлөт: биринчи жип өзүнүн an objectисин коюп, анын ордуна экинчисинин an objectисин алат, ал эми экинчиси, өз кезегинде, биринчинин an objectисин алып, өзүнүн нерсесин коёт. Башкача айтканда, биринчи жип Exchange() ыкмасын колдонот жана башка жип бир эле an objectте 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();
     }
   }
 }
}
Жип конструкторунда биз String түрүндөгү an objectтерди кабыл алган Exchanger an objectин аныктайбыз жана ишке киргизүүдө (иштетүү методунда ) биз анын 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();
Консол төмөнкүнү көрсөтөт:
Биринчи жип билдирүүнү кабыл алды: Экинчи жиптен кабар Экинчи жип кабарды алды: Биринчи жиптен кабар Экинчи жип кабарды алды: Экинчи жиптен кабар Биринчи жип билдирүүнү алды: Биринчи жиптен кабар Экинчи жип билдирүүнү алды: Биринчи жиптен билдирүү Биринчи жип билдирүүнү алды: Экинчи жиптен кабар... .
Бул жиптер ортосунда маалымат алмашуу ийгorктүү экенин билдирет.

112. Thread классы менен Runnable интерфейсинин айырмасы эмнеде?

Мен белгилеп кете турган биринчи нерсе , Thread бул класс, Runnable интерфейси, бул абдан ачык айырма =D Мен ошондой эле Thread Runnable (композиция) колдонот Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  13-3-бөлүкдеп айтам . Башкача айтканда, бизде эки жол бар:
  1. Thread дан мурастап , иштетүү ыкмасын жокко чыгарып, андан кийин бул an objectти түзүп, жипти start() ыкмасы аркылуу баштаңыз .

  2. Белгилүү бир класста Runnableди ишке ашырыңыз , анын run() ыкмасын ишке ашырыңыз , андан кийин Thread an objectин түзүп, Runnable интерфейсинин бул an objectинин ишке ашырылышын анын конструкторуна дайындаңыз . Аягында, start() ыкмасын колдонуп Thread an objectин ишке киргизиңиз .

Эмне артык? Бир аз ойлонуп көрөлү:
  • Runnable интерфейсин ишке ашырганда , жиптин жүрүм-турумун өзгөртпөйсүз. Чындыгында, сиз жипти иштетүү үчүн бир нерсе берип жатасыз. Ал эми бул биздин курамыбыз, ал өз кезегинде жакшы мамиле деп эсептелет.

  • Runnable ишке ашыруу классыңызга көбүрөөк ийкемдүүлүк берет. Эгер сиз Thread'тен мурастап алсаңыз , анда сиз аткарган аракет ар дайым жипте болот. Бирок Runnableди ишке ашырсаңыз, ал жөн гана жип болбошу керек. Анткени, сиз аны жипте иштете аласыз же аткаруучу кызматка өткөрүп бере аласыз. Мейли, же жөн гана бир жиптүү тиркемеде тапшырма катары бир жерге өткөрүп бериңиз.

  • 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();
Консолдук чыгаруу:
Биринчи жип - ишин баштады Биринчи жип - ишин аяктады Экинчи жип - ишин баштады Экинчи жип - ишин бүтүрдү Үчүнчү жип - ишин баштады Үчүнчү жип - ишин аяктады
Бул биздин милдетибизди бүтүрдүк дегенди билдирет. Андан кийин, биз түздөн-түз Junior деңгээлиндеги практикалык тапшырмаларга өтөбүз .

Практикалык тапшырмалар

114. Матрицанын диагоналдык суммасы (Leetcode маселеси)

Шарт: Негизги диагоналдагы бардык элементтердин жана негизги диагоналга кирбеген кошумча диагоналдагы бардык элементтердин суммасын эсептегиле. Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  13-5-бөлүк1. Формадагы матрица менен: 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 болушу керек. 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 - кошумча диагоналдын мамычаларын билдирүү үчүн. Эгерде негизги диагонал менен кошумча уяча дал келсе, анда сумманы эсептөөдө маанилердин бири эске алынbyte. Келгиле, шарттын матрицаларын колдонуп текшерели:
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. Берилген Тизме <Стринг> аттары. Ар бир аттын биринчи тамгасын алып салып, иреттелген тизмени айлантыңыз

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 жана андан жогорку versionсын колдонсок, биз жөн гана агым аркылуу чечимди көрсөтүшүбүз керек:
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. Саптын палиндром экенин текшериңиз

Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  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. Жөнөкөй сорттоо алгоритмин жазыңыз (Bubble, Selection же Shuttle). ал кантип жакшыртууга болот?

Ишке ашыруу үчүн жөнөкөй алгоритм катары мен тандоону сорттоону тандадым - Тандоо иреттөө:
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 итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
Жакшыртылган version төмөнкүдөй болот:
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 + "%");
Эки түр тең бир циклде башталган, анткени эгерде өзүнчө циклдер болсо, жогорудагы codeдон сорттоо экинчи орунга коюлгандан да жаман натыйжаларды көрсөтөт. Бул программанын "ысытып", андан кийин бир аз ылдамыраак иштегендигине байланыштуу. Бирок мен темадан бир аз алыстап жатам. Консолдо бул текшерүүнү беш жолу иштеткенден кийин, мен аткаруунун жогорулашын көрдүм: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.0919807057437016 , мен үчүн. абдан жакшы натыйжа. Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  13-7-бөлүк

120. Int тибиндеги литералды byte тибиндеги литерал менен түзүүнүн алгоритмин (иш-аракеттердин ырааттуулугун) жазыңыз. Эстутум эмне болорун түшүндүрүңүз

  1. byte мааниси intке айландырылат. Ал үчүн 1 byte эстутум бөлүнбөйт, бирок бардык int маанилери сыяктуу - 4, эгерде бул маани int стекинде жок болсо. Эгер бар болсо, ага шилтеме жөн гана кабыл алынат.

  2. Эки int мааниси кошулуп, үчүнчүсү алынат. Ал үчүн жаңы эс тутум бөлүмү бөлүнөт - 4 byte (же int стекинен учурдагы мааниге шилтеме алынат).

    Бул учурда, эки int эстутуму дагы эле ээлейт жана алардын маанилери тиешелүүлүгүнө жараша int стекинде сакталат.

Чынында, биздин тизмедеги Junior деңгээлиндеги суроолор ушул жерде аяктайт. Кийинки макаладан баштап биз Орто деңгээлдеги маселелерди түшүнөбүз. Орто деңгээлдеги суроолор да башталгыч деңгээлдеги иштеп чыгуучуларга жигердүү берorп жатканын белгилегим келет - Junior. Андыктан кабардар болуңуз. Ооба, бүгүнкү күндө бардыгы: көрүшкөнчө!Java иштеп чыгуучусу үчүн интервьюдан алынган суроолордун жана жооптордун анализи.  13-8-бөлүк
Сериядагы башка материалдар:
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION