111. Mövzular arasında məlumat mübadiləsi necə aparılır?
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:
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 Mən onu da deyim ki, Thread Runnable (kompozisiya) istifadə edir . Yəni iki yolumuz var:-
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 .
-
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 .
-
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).
113. T1, T2 və T3 ipləri var. Onları ardıcıl olaraq necə həyata keçirmək olar?
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ışı:
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. 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ışı:
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ışı:
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ışı:
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ışı:
118. Simin palindrom olub olmadığını yoxlayın
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ışı:
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ə.
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
-
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.
-
İ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.
GO TO FULL VERSION