111. How to exchange data between threads?
To exchange data between threads, you can use many different approaches and means: for example, use atomic variables, synchronized collections, and a semaphore. But to solve this problem, I will give an example with Exchanger . Exchanger is a synchronization class from the concurrent package that facilitates the exchange of elements between a pair of threads by creating a common synchronization point. Its use simplifies the exchange of data between two threads. The way it works is quite simple: it waits for two separate threads to call its exchange() method . Something like an exchange point is created between them: the first thread puts its object and receives the object of the other in return, and the latter, in turn, receives the object of the first and puts his own. That is, the first thread uses the exchange() method and is idle until another thread calls the exchange() method on the same object and data is exchanged between them. As an example, consider the following implementation of the Thread class :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();
}
}
}
}
In the thread constructor, we define an Exchanger object that accepts objects of type String , and at startup (in the run method ) we use its exchange() to exchange a message with another thread that uses this method in the same Exchanger . Let's run it in main :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
The console will display:
112. What is the difference between the Thread class and the Runnable interface?
The first thing I’ll note is that Thread is a class, Runnable is an interface, which is a very obvious difference =D I’ll also say that Thread uses Runnable (composition). That is, we have two ways:-
Inherit from Thread , override the run method, then create this object and start the thread through the start() method .
-
Implement Runnable in a certain class, implement its run() method , and then create a Thread object , assigning this object implementation of the Runnable interface to its constructor . Well, at the end, launch the Thread object using the start() method .
-
When you implement the Runnable interface , you do not change the behavior of the thread. Essentially you are just giving the thread something to run. And this is our composition, which in turn is considered a good approach.
-
implementing Runnable gives more flexibility to your class. If you inherit from Thread , then the action you perform will always be on the thread. But if you implement Runnable it doesn't have to be just a thread. After all, you can either run it in a thread or pass it to some executor service. Well, or just pass it somewhere as a task in a single-threaded application.
-
Using Runnable allows you to logically separate task execution from thread control logic.
-
In Java, only single inheritance is possible, so only one class can be extended. At the same time, the number of expandable interfaces is unlimited (well, not quite unlimited, but 65535 , but you are unlikely to ever hit this limit).
113. There are threads T1, T2 and T3. How to implement them sequentially?
The very first and simplest thing that comes to mind is using the join() method . It suspends execution of the current thread (that called the method) until the thread on which the method was called finishes executing. Let's create our own thread implementation: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 + " - закончил свою работу");
}
}
Let's start three such threads one by one using 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();
Console output:
Practical tasks
114. Matrix Diagonal Sum (Leetcode problem)
Condition: Calculate the sum of all elements on the main diagonal and all elements on the additional diagonal that are not part of the main diagonal. 1. With a matrix of the form: mat = [[1,2,3], [4,5,6], [7,8,9]] The output should be - 25 2. With a matrix - mat = [[1,1 ,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] The output should be - 8 3. With a matrix - mat = [[ 5]] The conclusion should be - 5 Pause reading and implement your decision. My solution would be the following: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;
}
Everything happens with one pass through the array, during which we have two indexes for the report: i - for reporting the rows of the array and columns of the main diagonal, j - for reporting the columns of the additional diagonal. If the cell of the main diagonal and the additional one coincide, then one of the values is ignored when calculating the sum. Let's check using the matrices from the condition:
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));
Console output:
115. Move Zeroes (Leetcode challenge)
Condition: In an integer array, move all 0's to the end, maintaining the relative order of non-zero elements. 1. With an array: [0,1,0,3,12] The output should be: [1,3,12,0,0] 2. With an array: [0] The output should be: [0] Pause and write my decision... My decision: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;// заполняем последние элементы массива нулями согласно счётчику нулей
}
}
Examination:
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));
Console output:
116. Given List <String> names. Remove the first letter from each name and rotate the sorted list
1. The first thing that comes to mind is the methods of the Collections class , which contains many auxiliary methods for 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. Also, if we use Java version 8 and higher, we simply have to show the solution via streams:
public static List<String> processTheList(List<String> nameList) {
return nameList.stream()
.map(x -> x.substring(1))
.sorted().collect(Collectors.toList());
}
Regardless of the chosen solution, the check may be as follows:
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));
Console output:
117. Flip the array
Solution 1 Again, the first thing that comes to mind is to use the methods of the auxiliary utility class Collections . But since we have an array, we first need to convert it into a collection (list):public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
Solution 2 Since the question was about an array, I think it is necessary to show the solution without using ready-made functionality out of the box, and so to speak, according to the classics:
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;
}
Examination:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Console output:
118. Check if a string is a palindrome
Solution 1 It’s worth immediately remembering StringBuilder : it is more flexible and rich in various methods compared to regular String . We're especially interested in the reverse method :public static boolean isPalindrome(String string) {
string = string.toLowerCase(); //приводит всю строку к нижнему регистру
StringBuilder builder = new StringBuilder();
builder.append(string);
builder.reverse(); // перевочиваем строку методом Builder-а
return (builder.toString()).equals(string);
}
Solution: The next approach will be without using the “loopholes” out of the box. We compare the characters from the back of the string with the corresponding characters from the front:
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;
}
And we check both approaches:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Console output:
119. Write a simple sorting algorithm (Bubble, Selection or Shuttle). How can it be improved?
As a simple algorithm for implementation, I chose selection sorting - 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 итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
The improved version would look like this:
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;
}
Well, now we need to make sure whether sorting has really improved. Let's compare performance:
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 + "%");
Both sorts started in the same cycle, because if there were separate loops, sorting from in the code above would show a worse result than if it was placed second. This is due to the fact that the program “warms up” and then works a little faster. But I'm going a little off topic. After five runs of this check in the console, I saw an increase in performance by: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% As for me, this is a pretty good result.
120. Write an algorithm (sequence of actions) for composing a literal of type int with a literal of type byte. Explain what happens to memory
-
byte value is converted to int. Not 1 byte of memory will be allocated for it, but like all int values - 4, if this value is not yet on the int stack. If there is, a link to it will simply be received.
-
Two int values will be added and the third will be obtained. A new memory section will be allocated for it - 4 bytes (or a reference will be received from the int stack to the existing value).
In this case, the memory of two ints will still be occupied, and their values will be stored on the int stack, respectively.
GO TO FULL VERSION