Параллельные операции над массивами

Последнее обновление: 30.04.2018

В JDK 8 к классу Arrays было добавлено ряд методов, которые позволяют в параллельном режиме совершать обработку элементов массива. И хотя данные методы формально не входят в Stream API, но реализуют схожую функциональность, что и параллельные потоки:

  • parallelPrefix(): вычисляет некоторое значение для элементов массива (например, сумму элементов)

  • parallelSetAll(): устанавливает элементы массива с помощью лямбда-выражения

  • parallelSort(): сортирует массив

Используем метод parallelSetAll() для установки элементов массива:

import java.util.Arrays;
public class Program {

    public static void main(String[] args) {
		
        int[] numbers = initializeArray(6);
        for(int i: numbers)
            System.out.println(i);
        
    } 
    public static int[] initializeArray(int size) {
        int[] values = new int[size];
        Arrays.parallelSetAll(values, i -> i*10);
        return values;
    }
}

В метод Arrays.parallelSetAll передается два параметра: изменяемый массив и функция, которая устанавливает элементы массива. Эта функция перебирает все элементы и в качестве параметра получает индекс текущего перебираемого элемента. Выражение i -> i*10 означает, что по каждому индексу в массиве будет хранится число, равное i * 10. В итоге мы получим следующий вывод:

0
10
20
30
40
50

Рассмотрим более сложный пример. Пусть у нас есть следующий класс Phone:

class Phone{
    
    private String name;
    private int price;
    
    public Phone(String name, int price){
        this.name=name;
        this.price = price;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String val) {
        this.name=val;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int val) {
        this.price=val;
    }
}

Теперь произведем манипуляции с массивом объектов Phone:

Phone[] phones = new Phone[]{new Phone("iPhone 8", 54000), 
    new Phone("Pixel 2", 45000),
    new Phone("Samsung Galaxy S9", 40000),
    new Phone("Nokia 9", 32000)};
        
Arrays.parallelSetAll(phones, i -> {
    phones[i].setPrice(phones[i].getPrice()-10000); 
    return phones[i];
});
        
for(Phone p: phones)
    System.out.printf("%s - %d \n", p.getName(), p.getPrice());

Теперь лямбда-выражение в методе Arrays.parallelSetAll представляет блок кода. И так как лямбда-выражение должно возвращать объект, то нам надо явным образом использовать оператор return. В этом лямбда-выражении опять же функция получает индексы перебираемых элементов, и по этим индексам мы можем обратиться к элементам массива и их изменить. Конкретно в данном случае происходит уменьшение цены смартфонов на 10000 единиц. В итоге мы получим следующий консольный вывод:

iPhone 8 - 44000 
Pixel 2 - 35000 
Samsung Galaxy S9 - 30000 
Nokia 9 - 22000 

Сортировка

Отсортируем массив чисел в параллельном режиме:

int[] nums = {30, -4, 5, 29, 7, -8};
Arrays.parallelSort(nums);
for(int i: nums)
    System.out.println(i);

Метод Arrays.parallelSort() в качестве параметра принимает массив и сортирует его по возрастанию:

-8
-4
5
7
29
30

Если же нам надо как-то по-другому отсортировать объекты, например, по модулю числа, или у нас более сложные объекты, то мы можем создать свой компаратор и передать его в качестве второго параметра в Arrays.parallelSort(). Например, возьмем выше определенный класс Phone и создадим для него компаратор:

import java.util.Arrays;
import java.util.Comparator;
public class Program {
 
    public static void main(String[] args) {
         
        Phone[] phones = new Phone[]{new Phone("iPhone 8", 54000), 
		new Phone("Pixel 2", 45000),
		new Phone("Samsung Galaxy S9", 40000),
		new Phone("Nokia 9", 32000)};
        
        Arrays.parallelSort(phones,new PhoneComparator());
        
         for(Phone p: phones)
            System.out.println(p.getName());
    }
}
class PhoneComparator implements Comparator<Phone>{
 
    public int compare(Phone a, Phone b){
     
        return a.getName().toUpperCase().compareTo(b.getName().toUpperCase());
    }
}

Метод parallelPrefix

Метод parallelPrefix() походит для тех случаев, когда надо получить элемент массива или объект того же типа, что и элементы массива, который обладает некоторыми признаками. Например, в массиве чисел это может быть максимальное, минимальное значения и т.д. Например, найдем произведение чисел:

int[] numbers = {1, 2, 3, 4, 5, 6};
Arrays.parallelPrefix(numbers, (x, y) -> x * y);

for(int i: numbers)
    System.out.println(i);

Мы получим следующий результат:

1
2
6
24
120
720

То есть, как мы видим из консольного вывода, лямбда-выражение из Arrays.parallelPrefix, которое представляет бинарную функцию, получает два элемента и выполняет над ними операцию. Результат операции сохраняется и передается в следующий вызов бинарной функции.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850