Есть ли какая-нибудь быстрая функция для выполнения следующего кода?
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]
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. Пример:
- Создайте поток индексов, к которым мы хотим получить доступ
A
/B
; а именно, от 0 доsize of A or B
. - Сопоставьте индексы с различиями элементов
A
иB
. - Соберите (потоковые) результаты в виде списка с именем
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 — это инструмент разработки, обеспечивающий правильную функциональность вашего кода. Если утверждения терпят неудачу, они выдают исключение. Они будут работать только в том случае, если вы правильно компилируете a> и включить их.
Обратите внимание, что утверждения не заменяют правильную проверку! У Oracle есть сводка того, когда использовать утверждения.
В приведенных выше примерах я использую утверждения главным образом для семантического описания того, что (примерно) "следующий код работает только при условии утверждения".
Примитивы и коробочные значения
Это требуется в местах, где нельзя использовать примитивы или, скорее, там, где ожидается Object
. Примерами являются Общие, такие как List
, для которых вы найдете простые объяснения в < href="https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html" rel="nofollow noreferrer">руководство Oracle по автоматической упаковке.
Будьте особенно осторожны, если в вашем коде ссылки на такие классы-оболочки могут быть нулевыми: Java не может распаковать null в примитив, вместо этого вызовет исключение во время выполнения! В выше код, такой сценарий не может произойти.
В обоих подходах
Поскольку массивы более удобочитаемы и статически компилируются, я решил предоставить начальные значения списков в виде массивов. Моя реализация пришла со следующими требованиями:
- Для построения
ArrayList
с начальными значениями требуетсяCollection
.[1] Arrays.asList()
возвращаетCollection
, но требует массив не-примитивов (здесь: массив упакованных значений; далее: упакованный массив).[2]- Инициализация упакованного массива (
Double[]
) требует, чтобы элементы относились к соответствующему примитивному типу; нам нужно предоставить числа в видеdouble
.[3] - Объявление числовых литералов как
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 "Справочники по методам").
Затем такой массив можно использовать для создания списка, как обсуждалось ранее.
Вам нужно перебрать каждый элемент массива и вычесть его из подчиненных элементов в другом массиве. Ниже приведен фрагмент рабочего кода.
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]
Вы можете проверить этот старый вопрос и его первый ответ, который рекомендует использовать Stream API. Как вычесть значения двух списков/массивов в Java?
Похожие вопросы
Связанные вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.
0 = 1 - 0
, т.е.[first of C] = [first of A] - [first of B]
?[1,1,1,1,1]
. Прости за это. Я имею в виду, есть ли у нас какие-либо функции для выполнения тех же действий, что и в моем примере кода?A
иB
всегда иметь одинаковый размер?A.size()==B.size()
, всегда.