Есть ли какая-нибудь быстрая функция для выполнения следующего кода?

ArrayList<Double> A = new ArrayList<>();
ArrayList<Double> B = new ArrayList<>();
//ignore adding steps
// A = [1,2,3,4,5]
// B = [0,1,2,3,4]
C = A - B
// get C = [1,1,1,1,1]
0
Allonsy Jia 27 Ноя 2022 в 06:45
2
Можете показать, что вы пробовали? Вы можете реализовать это императивно (например, с помощью циклов for) или функционально (например, с помощью Java Streams).
 – 
Oskar Grosser
27 Ноя 2022 в 07:01
Также: можете ли вы уточнить, что вы подразумеваете под «выполнить следующий код», «игнорировать добавление шагов» и ваш расчет 0 = 1 - 0, т.е. [first of C] = [first of A] - [first of B]?
 – 
Oskar Grosser
27 Ноя 2022 в 07:08
О, это действительно опечатка. C должно быть [1,1,1,1,1]. Прости за это. Я имею в виду, есть ли у нас какие-либо функции для выполнения тех же действий, что и в моем примере кода?
 – 
Allonsy Jia
27 Ноя 2022 в 07:29
Будут ли A и B всегда иметь одинаковый размер?
 – 
Oskar Grosser
27 Ноя 2022 в 07:55
Да, A.size()==B.size(), всегда.
 – 
Allonsy Jia
27 Ноя 2022 в 09:30

3 ответа

Лучший ответ

TL;DR: см. два полных примера кода ниже и заголовки их разделов для контекста.

Подходы

Императивный подход

Императивное программирование обычно описывается как "инструктирование отдельных шагов для достижения определенную цель».

Мы можем использовать циклы for — языковую функцию связанный с императивным программированием — для перебора списков A и B, а различия их элементов собрать в список C:

import java.util.ArrayList;
import java.util.Arrays;

public class ImperativeApproach {
  public static void main(String[] args) {
    ArrayList<Double> A = new ArrayList<>(Arrays.asList(new Double[] {1d,2d,3d,4d,5d}));
    ArrayList<Double> B = new ArrayList<>(Arrays.asList(new Double[] {0d,1d,2d,3d,4d}));
    
    ArrayList<Double> C = new ArrayList<>();
    
    assert A.size() == B.size();
    int size = A.size();
    for (int i = 0; i < size; ++i) {
      C.add(A.get(i) - B.get(i));
    }
    
    System.out.println(C); // [1.0, 1.0, 1.0, 1.0, 1.0]
  }
}

Функциональный подход

Функциональное программирование более декларативно, чем императивное: оно может можно описать как «запрос определенного результата при выполнении определенных шагов».

Характеристики функционального программирования обычно включают функции высшего порядка и функционирует как граждане первого класса, композиция функций, лямбда-выражения и многое другое.

Кроме того: обратите внимание, что и императивное, и функциональное программирование — это всего лишь парадигмы; способы классификации кода или языков по определенным «чертам». Пример: с добавлением потоков (см. ниже) на Java стало проще писать функциональный код на Java.

Как упоминалось ранее, мы можем использовать потоки Java. Пример:

  1. Создайте поток индексов, к которым мы хотим получить доступ A/B; а именно, от 0 до size of A or B.
  2. Сопоставьте индексы с различиями элементов A и B.
  3. Соберите (потоковые) результаты в виде списка с именем C.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.IntStream;

public class FunctionalApproach {
  public static void main(String[] args) {
    ArrayList<Double> A = new ArrayList<>(Arrays.asList(new Double[] {1d,2d,3d,4d,5d}));
    ArrayList<Double> B = new ArrayList<>(Arrays.asList(new Double[] {0d,1d,2d,3d,4d}));
    
    assert A.size() == B.size();
    int size = A.size();
    Double[] arrayC = IntStream.range(0, size)
        .mapToDouble((index) -> A.get(index) - B.get(index))
        .boxed()
        .toArray(Double[]::new);
    
    ArrayList<Double> C = new ArrayList<>(Arrays.asList(arrayC));
    
    System.out.println(C); // [1.0, 1.0, 1.0, 1.0, 1.0]
  }
}

(@GrzegorzPiwowarek предоставил альтернативные и, возможно, лучшие реализации на похожий, но более старый вопрос.)

Дополнение

В приведенных выше примерах могли быть незнакомые выражения, которые я попытаюсь объяснить в этом разделе.

Утверждения

Утверждения в Java — это инструмент разработки, обеспечивающий правильную функциональность вашего кода. Если утверждения терпят неудачу, они выдают исключение. Они будут работать только в том случае, если вы правильно компилируете и включить их.

Обратите внимание, что утверждения не заменяют правильную проверку! У Oracle есть сводка того, когда использовать утверждения.

В приведенных выше примерах я использую утверждения главным образом для семантического описания того, что (примерно) "следующий код работает только при условии утверждения".

Примитивы и коробочные значения

(Auto-)boxing в Java относится к преобразованию примитивного значения в соответствующий класс-оболочку.

Это требуется в местах, где нельзя использовать примитивы или, скорее, там, где ожидается Object. Примерами являются Общие, такие как List, для которых вы найдете простые объяснения в < href="https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html" rel="nofollow noreferrer">руководство Oracle по автоматической упаковке.

Будьте особенно осторожны, если в вашем коде ссылки на такие классы-оболочки могут быть нулевыми: Java не может распаковать null в примитив, вместо этого вызовет исключение во время выполнения! В выше код, такой сценарий не может произойти.

В обоих подходах

Поскольку массивы более удобочитаемы и статически компилируются, я решил предоставить начальные значения списков в виде массивов. Моя реализация пришла со следующими требованиями:

  1. Для построения ArrayList с начальными значениями требуется Collection.[1]
  2. Arrays.asList() возвращает Collection, но требует массив не-примитивов (здесь: массив упакованных значений; далее: упакованный массив).[2]
  3. Инициализация упакованного массива (Double[]) требует, чтобы элементы относились к соответствующему примитивному типу; нам нужно предоставить числа в виде double.[3]
  4. Объявление числовых литералов как double требует, чтобы мы добавляли к ним суффикс d (или использовали десятичную точку).[4]

Это означает, что этот неверный код:

ArrayList<double> A = new double[]{1, 2, 3, 4, 5};

Необходимо написать, например. следующим образом:

ArrayList<Double> A = new ArrayList<>(Arrays.asList(new Double[]{1d, 2d, 3d, 4d, 5d}));

В императивном подходе

В примере с императивным подходом дальнейших изменений кода не требуется. Добавление чисел в список C автоматически упаковывает числа, потому что здесь не используется промежуточный объект с определенными ограничениями, такими как new Double[].

В функциональном подходе

В примере Потоки IntStream.mapToDouble()возвращает DoubleStream (поток, действующий на примитивы). Его toArray() вернет примитивный массив (double[]), который мы не можем легко предоставить для построения списка C, как упоминалось ранее.

Поэтому сначала нам нужно упаковать его (см. DoubleStream.boxed()), который возвращает Stream<Double>. Затем элементы этого потока можно собрать в упакованный массив (предоставив конструктор массива Double[]::new; см. Руководство Oracle "Справочники по методам").

Затем такой массив можно использовать для создания списка, как обсуждалось ранее.

2
Oskar Grosser 27 Ноя 2022 в 11:06
Ваш ответ действительно полезен! Благодарность!
 – 
Allonsy Jia
27 Ноя 2022 в 11:19

Вам нужно перебрать каждый элемент массива и вычесть его из подчиненных элементов в другом массиве. Ниже приведен фрагмент рабочего кода.

import java.util.*;
class HelloWorld {
    public static void main(String[] args) {
       int[] list1 = {1,2,3,4,5};
        int[] list2 = {0,1,2,3,4};
        int[] list3 = {0,0,0,0,0};
        
        for(int i =0; i< list1.length; i++)
        {
          list3[i] = list1[i] - list2[i];
        }
        
       for(int i =0; i< list3.length; i++)
       {
          System.out.print(list3[i]);
       }
        
    }
}

Что даст вам следующий вывод:

[1, 1, 1, 1, 1]

2
Selaka Nanayakkara 27 Ноя 2022 в 07:15
Собственно об этом я и думаю. Я просто хочу знать, есть ли у нас какие-либо встроенные функции в Java для этого.
 – 
Allonsy Jia
27 Ноя 2022 в 07:30
Я не думаю, что есть какая-либо встроенная функция для удовлетворения этого пользовательского требования. Если это отвечает на ваш вопрос, не могли бы вы отметить его как правильный и проголосовать за него. Заранее благодарим за оказанную поддержку.
 – 
Selaka Nanayakkara
27 Ноя 2022 в 08:10
Спасибо за ваш ответ, я подожду 2 дня других ответов, прежде чем аннотировать ваш ответ как ответ.
 – 
Allonsy Jia
27 Ноя 2022 в 09:31
1
Нет, "встроенной функции" нет. И да, я предпочитаю именно то, что предложил Селака Нанаяккара: цикл for(). Но, как предложил выше Оскар Гроссер, Java Streams также является вариантом (для Java 8 и выше).
 – 
paulsm4
27 Ноя 2022 в 10:37

Вы можете проверить этот старый вопрос и его первый ответ, который рекомендует использовать Stream API. Как вычесть значения двух списков/массивов в Java?

2
Ashkanxy 27 Ноя 2022 в 10:44
Отличная находка! Но это должен быть комментарий к вопросу, а не отдельный ответ. (И я не должен модерировать.)
 – 
Oskar Grosser
27 Ноя 2022 в 10:45
Спасибо большое!
 – 
Allonsy Jia
27 Ноя 2022 в 11:17