Я играю с ленивыми функциональными операциями в Java SE 8, и я хочу map
индексировать i
пару / кортеж (i, value[i])
, затем filter
на основе второго value[i]
элемента и, наконец, выводить только индексы.
Должен ли я все еще страдать так: Что эквивалентно паре C ++ <L, R> в Java? в смелую новую эпоху лямбд и ручьев?
Обновление: я представил довольно упрощенный пример, в котором есть аккуратное решение, предложенное @dkatzel в одном из ответов ниже. Однако это не обобщает. Поэтому позвольте мне добавить более общий пример:
package com.example.test;
import java.util.ArrayList;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
boolean [][] directed_acyclic_graph = new boolean[][]{
{false, true, false, true, false, true},
{false, false, false, true, false, true},
{false, false, false, true, false, true},
{false, false, false, false, false, true},
{false, false, false, false, false, true},
{false, false, false, false, false, false}
};
System.out.println(
IntStream.range(0, directed_acyclic_graph.length)
.parallel()
.mapToLong(i -> IntStream.range(0, directed_acyclic_graph[i].length)
.filter(j -> directed_acyclic_graph[j][i])
.count()
)
.filter(n -> n == 0)
.collect(() -> new ArrayList<Long>(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2))
);
}
}
Это дает неправильный вывод, [0, 0, 0]
который соответствует количеству всех трех столбцов false
. Что мне нужно, это индексы этих трех столбцов. Правильный вывод должен быть [0, 2, 4]
. Как я могу получить этот результат?
java
lambda
functional-programming
java-8
java-stream
некромант
источник
источник
AbstractMap.SimpleImmutableEntry<K,V>
течение многих лет ... Но в любом случае, вместо отображения ,i
чтобы(i, value[i])
только для фильтрации поvalue[i]
и отображению обратноi
: почему не только фильтр,value[i]
в первую очередь, без отображения?i
в потоке. Мне тоже нужныvalue[i]
критерии. Вот почему мне нужно(i, value[i])
[0, 2, 4]
?Ответы:
ОБНОВЛЕНИЕ: Этот ответ является ответом на оригинальный вопрос: есть ли в Java SE 8 пары или кортежи? (И неявно, если нет, почему бы и нет?) ОП обновил вопрос более полным примером, но кажется, что его можно решить без использования какой-либо структуры Pair. [Примечание от ОП: вот другой правильный ответ .]
Краткий ответ: нет. Вы должны либо свернуть свою собственную, либо ввести одну из нескольких библиотек, которая ее реализует.
Наличие
Pair
класса в Java SE было предложено и отклонено по крайней мере один раз. Смотрите эту ветку обсуждения в одном из списков рассылки OpenJDK. Компромиссы не очевидны. С одной стороны, существует много реализаций Pair в других библиотеках и в коде приложения. Это демонстрирует необходимость, и добавление такого класса в Java SE увеличит повторное использование и совместное использование. С другой стороны, наличие класса Pair увеличивает соблазн создания сложных структур данных из пар и коллекций без создания необходимых типов и абстракций. (Это парафраз сообщения Кевина Буриллиона из этой ветки .)Я рекомендую всем прочитать всю эту электронную почту. Это удивительно проницательно и не имеет никакого пламени. Это довольно убедительно. Когда он начался, я подумал: «Да, в Java SE должен быть класс Pair», но к тому времени, когда поток достиг своего конца, я передумал.
Однако обратите внимание, что JavaFX имеет класс javafx.util.Pair . API JavaFX развивались отдельно от API Java SE.
Как видно из связанного вопроса, что является эквивалентом пары C ++ в Java? Существует довольно большое пространство дизайна, окружающее, по-видимому, такой простой API. Должны ли объекты быть неизменными? Должны ли они быть сериализуемыми? Должны ли они быть сопоставимы? Класс должен быть окончательным или нет? Стоит ли заказывать два элемента? Должен ли это быть интерфейс или класс? Зачем останавливаться на парах? Почему не тройки, четверки или N-кортежи?
И, конечно же, существует неизбежная система именования элементов:
Одна большая проблема, которая едва упоминалась, - это отношение пар к примитивам. Если у вас есть
(int x, int y)
геодезический , которая представляет собой точку в 2D пространстве, представляя это какPair<Integer, Integer>
потребляет три объекта вместо двух 32-битных слов. Кроме того, эти объекты должны находиться в куче и подвергаться GC-нагрузке.Казалось бы, ясно, что, как и в Streams, важно, чтобы существовали примитивные специализации для пар. Хотим ли мы увидеть:
Даже
IntIntPair
если все равно потребуется один объект в куче.Это, конечно, напоминает распространение функциональных интерфейсов в
java.util.function
пакете в Java SE 8. Если вы не хотите раздутого API, какие из них вы бы оставили? Вы также можете утверждать, что этого недостаточно, и что специализации, скажем, такжеBoolean
следует добавить.У меня такое ощущение, что если бы Java давно добавила класс Pair, это было бы просто или даже упрощенно, и это не удовлетворило бы многие варианты использования, которые мы предполагаем сейчас. Учтите, что если бы Pair был добавлен во временные рамки JDK 1.0, он, вероятно, был бы изменчивым! (Посмотрите на java.util.Date.) Будут ли люди довольны этим? Я предполагаю, что если бы в Java существовал класс Pair, он был бы своего рода не очень полезным, и каждый все равно будет крутить свои собственные, чтобы удовлетворить свои потребности, во внешних библиотеках были бы различные реализации Pair и Tuple, и люди все еще будут спорить / обсуждать, как исправить класс Pair в Java. Другими словами, вроде в том же месте, в котором мы находимся сегодня.
Между тем, продолжается работа по решению фундаментальной проблемы, которая заключается в улучшении поддержки в JVM (и в конечном итоге в языке Java) для типов значений . Смотрите этот документ о состоянии ценностей . Это предварительная, умозрительная работа, и она охватывает только вопросы с точки зрения JVM, но за ней уже стоит немало идей. Конечно, нет никаких гарантий, что это войдет в Java 9 или когда-нибудь проникнет, но это показывает текущее направление мышления по этой теме.
источник
Pair<T,U>
. Поскольку дженерики должны быть ссылочного типа. Любые примитивы будут упакованы, когда они будут сохранены. Для хранения примитивов вам действительно нужен другой класс.valueOf
должны были быть единственным способом получить экземпляр в штучной упаковке. Но они были там начиная с Java 1.0 и, вероятно, не стоит пытаться изменить на этом этапе.Pair
илиTuple
класс с фабричным методом, создающим необходимые классы специализации (с оптимизированным хранилищем) прозрачно в фоновом режиме. В конце концов, лямбды делают именно это: они могут захватывать произвольное количество переменных произвольного типа. А теперь представьте себе языковую поддержку, позволяющую создавать соответствующий класс кортежей во время выполнения, запускаемыйinvokedynamic
инструкцией…invokedynamic
фабрикой на основе, похожей на создание лямбды, такая последующая модернизация не будет проблемой. Кстати, лямбды тоже не имеют идентичности. Как прямо указано, идентичность, которую вы можете воспринимать сегодня, является артефактом текущей реализации.Вы можете взглянуть на эти встроенные классы:
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
источник
SimpleImmutableEntry
гарантирует только то, что ссылки, хранящиеся в папкеEntry
not, не изменяются, а не то, что поля связанных объектовkey
иvalue
объектов (или объектов, на которые они ссылаются) не меняются.К сожалению, в Java 8 не было пар или кортежей. Конечно, вы всегда можете использовать org.apache.commons.lang3.tuple (который я лично использую в сочетании с Java 8) или вы можете создавать свои собственные обертки. Или используйте Карты. Или что-то в этом роде, как объясняется в принятом ответе на тот вопрос, с которым вы связаны.
ОБНОВЛЕНИЕ: JDK 14 представляет записи в качестве функции предварительного просмотра. Это не кортежи, но их можно использовать для решения многих проблем. В вашем конкретном примере сверху это может выглядеть примерно так:
При компиляции и запуске с JDK 14 (на момент написания, это ранняя сборка с доступом) с использованием
--enable-preview
флага вы получите следующий результат:источник
Похоже, что полный пример может быть решен без использования какой-либо структуры Pair. Ключ заключается в том, чтобы фильтровать индексы столбцов с помощью предиката, проверяющего весь столбец, вместо сопоставления индексов столбцов с количеством
false
записей в этом столбце.Код, который делает это здесь:
Это приводит к выводу,
[0, 2, 4]
который я считаю правильным результатом, запрошенным OP.Также обратите внимание на
boxed()
операцию, которая упаковываетint
значения вInteger
объекты. Это позволяет использовать ранее существовавшийtoList()
коллектор вместо того, чтобы выписывать функции коллектора, которые сами выполняют сборку.источник
true
). Соответственно, я приму ваш другой ответ как правильный, но также укажу на этот! Большое спасибо :)Vavr (ранее называемый Javaslang) ( http://www.vavr.io ) также предоставляет кортежи (размером до 8). Вот этот javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .
Это простой пример:
Почему сам JDK не имел простых кортежей до сих пор, для меня загадка. Написание классов-обёрток кажется повседневным делом.
источник
Начиная с Java 9, вы можете создавать экземпляры
Map.Entry
проще, чем раньше:Map.entry
возвращает неизменяемоеEntry
и запрещает нули.источник
Поскольку вы заботитесь только об индексах, вам вообще не нужно отображать кортежи. Почему бы просто не написать фильтр, который использует элементы поиска в вашем массиве?
источник
Да.
Map.Entry
может быть использован в качествеPair
.К сожалению, это не помогает с потоками Java 8, поскольку проблема заключается в том, что, хотя лямбда-выражения могут принимать несколько аргументов, язык Java позволяет возвращать только одно значение (объект или тип примитива). Это подразумевает, что всякий раз, когда у вас есть поток, вы в конечном итоге получаете один объект из предыдущей операции. Это недостаток в языке Java, потому что, если поддерживаются несколько возвращаемых значений И потоки поддерживают их, у нас могут быть гораздо более приятные нетривиальные задачи, выполняемые потоками.
До тех пор, есть только небольшая польза.
РЕДАКТИРОВАТЬ 2018-02-12: Работая над проектом, я написал вспомогательный класс, который помогает обрабатывать особый случай наличия идентификатора раньше в потоке, который вам нужен позже, но промежуточная часть потока не знает об этом. Пока я не смогу выпустить его самостоятельно, он доступен на IdValue.java с модульным тестом на IdValueTest.java.
источник
Eclipse Collections имеет
Pair
и все комбинации пар примитивов / объектов (для всех восьми примитивов).Tuples
Завод может создавать экземплярыPair
, иPrimitiveTuples
завод может быть использован для создания всех комбинаций примитивного пара / объектов.Мы добавили их до выпуска Java 8. Они были полезны для реализации итераторов ключ / значение для наших примитивных карт, которые мы также поддерживаем во всех комбинациях примитивов / объектов.
Если вы готовы добавить дополнительные накладные расходы библиотеки, вы можете использовать принятое решение Стюарта и собрать результаты в примитив,
IntList
чтобы избежать упаковки. Мы добавили новые методы в Eclipse Collections 9.0, чтобы позволитьInt/Long/Double
создавать коллекции изInt/Long/Double
потоков.Примечание: я являюсь коммиттером для Eclipse Collections.
источник