Есть ли в Java SE 8 пары или кортежи?

Я играю с ленивыми функциональными операциями в Java SE 8, и я хочу map индекс i с парой / кортежем (i, value[i]) , затем filter на основе второго элемента value[i] и, наконец, выводить просто индексы.

Должен ли я по-прежнему страдать от этого: что эквивалентно C ++ Pair в Java? в смелую новую эру lambda и ручьев?

Обновление: я представил довольно упрощенный пример, в котором есть опрятное решение, предлагаемое @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(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2)) ); } } 

Это дает неверный вывод [0, 0, 0] который соответствует подсчетам для трех столбцов, которые являются false . Мне нужны индексы этих трех столбцов. Правильный выход должен быть [0, 2, 4] . Как я могу получить этот результат?

ОБНОВЛЕНИЕ: Этот ответ отвечает на исходный вопрос: есть ли у Java SE 8 пары или кортежи? (И неявно, если нет, то почему?) ОП обновил вопрос более полным примером, но похоже, что он может быть решен без использования какой-либо структуры пар. [Примечание от OP: вот другой правильный ответ .]


Короткий ответ: нет. Вы либо должны катиться самостоятельно, либо вводить одну из нескольких библиотек, которые ее реализуют.

Наличие classа Pair в Java SE было предложено и отклонено хотя бы один раз. См. Эту дискуссионную тему в одном из списков рассылки OpenJDK. Компромиссы не очевидны. С одной стороны, существует много реализаций Pair в других библиотеках и в коде приложения. Это демонстрирует необходимость, и добавление такого classа в Java SE увеличит повторное использование и совместное использование. С другой стороны, наличие classа Pair добавляет к соблазну создания сложных структур данных из пар и коллекций без создания необходимых типов и абстракций. (Это парафраз из послания Кевина Буриллиона из этой темы .)

Я рекомендую всем прочитать весь stream электронной почты. Это замечательно проницательно и не обладает огнем. Это довольно убедительно. Когда это началось, я подумал: «Да, в Java SE должен быть class Pair», но к тому времени, когда stream достиг своего конца, я передумал.

Однако обратите внимание, что JavaFX имеет class javafx.util.Pair . API API JavaFX развивались отдельно от API Java SE.

Как видно из связанного вопроса Что такое эквивалент C ++ Pair в Java? существует довольно большое пространство для проектирования, окружающее, по-видимому, такой простой API. Должны ли объекты быть неизменными? Должны ли они быть сериализуемыми? Должны ли они быть сопоставимыми? Должен ли class быть окончательным или нет? Должны ли быть заказаны два элемента? Должен ли он быть интерфейсом или classом? Зачем останавливаться на парах? Почему бы не тройки, квадроциклы или N-кортежи?

И, конечно же, есть неизбежное именование для элементов:

  • (a, b)
  • (первая секунда)
  • (лево право)
  • (автомобиль, cdr)
  • (foo, bar)
  • и т.п.

Одна большая проблема, о которой почти не упоминалось, – это отношение Пары к примитивам. Если у вас есть (int x, int y) datum, которая представляет точку в 2D-пространстве, представляя это как Pair потребляет три объекта вместо двух 32-битных слов. Кроме того, эти объекты должны находиться в куче и будут нести накладные расходы GC.

Казалось бы, что, подобно Streams, было бы важно, чтобы там были примитивные специализации для пар. Мы хотим видеть:

 Pair ObjIntPair ObjLongPair ObjDoublePair IntObjPair IntIntPair IntLongPair IntDoublePair LongObjPair LongIntPair LongLongPair LongDoublePair DoubleObjPair DoubleIntPair DoubleLongPair DoubleDoublePair 

Даже IntIntPair все равно потребует один объект в куче.

Это, конечно, напоминает распространение функциональных интерфейсов в пакете java.util.function в Java SE 8. Если вам не нужен раздутый API, какие из них вы бы оставили? Вы также можете утверждать, что этого недостаточно, и что специализации, например, Boolean должны быть добавлены.

Я чувствую, что если Java уже давно добавила class Pair, это было бы просто или даже упрощенно, и это не удовлетворило бы многие из вариантов использования, которые мы сейчас рассматриваем. Учтите, что если бы пара была добавлена ​​в JDK 1.0, это, вероятно, было бы изменчивым! (Посмотрите на java.util.Date.) Разве люди были бы рады этому? Я предполагаю, что если бы на Java был class Pair, он был бы добрым-не-полезным, и все все равно будут кататься самостоятельно, чтобы удовлетворить их потребности, были бы различные реализации Pair и Tuple во внешних библиотеках, и люди все равно будут обсуждать / обсуждать, как исправить class пары Pair. Другими словами, вид того же самого места, в котором мы находимся сегодня.

Между тем, идет некоторая работа по решению фундаментальной проблемы, которая лучше поддерживает JVM (и, в конечном счете, язык Java) для типов значений . См. Документ « Состояние ценностей» . Это предварительная, спекулятивная работа, и она охватывает только проблемы с точки зрения JVM, но за этим уже стоит много размышлений. Конечно, нет никаких гарантий, что это войдет в Java 9 или когда-либо попадет куда угодно, но это покажет текущее направление мышления по этой теме.

Вы можете посмотреть на эти встроенные classы:

  • AbstractMap.SimpleEntry
  • AbstractMap.SimpleImmutableEntry

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

Код, который делает это, находится здесь:

  System.out.println( IntStream.range(0, acyclic_graph.length) .filter(i -> IntStream.range(0, acyclic_graph.length) .noneMatch(j -> acyclic_graph[j][i])) .boxed() .collect(toList())); 

В результате получается результат [0, 2, 4] который, я думаю, правильный результат, запрошенный OP.

Также обратите внимание на операцию boxed() которая int значения int в объекты Integer . Это позволяет использовать уже существующий toList() вместо того, чтобы записывать функции коллектора, которые сами делают бокс.

К сожалению, на Java 8 не вводились пары или кортежи. Вы всегда можете использовать org.apache.commons.lang3.tuple, конечно (что лично я использую в сочетании с Java 8), или вы можете создавать свои собственные обертки. Или используйте Карты. Или что-то вроде этого, как объясняется в принятом ответе на тот вопрос, с которым вы связались.

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

  int[] value = ... IntStream.range(0, value.length) .filter(i -> value[i] > 30) //or whatever filter you want .forEach(i -> System.out.println(i)); 

Вавр (ранее называемый Javaslang) ( http://www.vavr.io ) также предоставляет кортежи (размер 8). Вот javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .

Это простой пример:

 Tuple2 entry = Tuple.of(1, "A"); Integer key = entry._1; String value = entry._2; 

Почему сам JDK не пришел с простыми типами кортежей, и теперь для меня это тайна. Написание classов-оболочек – это ежедневный бизнес.

Да.

Map.Entry может использоваться как Pair .

К сожалению, это не помогает в streamах Java 8, поскольку проблема заключается в том, что хотя lambdas может принимать несколько аргументов, язык Java позволяет только возвращать одно значение (объект или примитивный тип). Это означает, что всякий раз, когда у вас есть stream, вы получаете один объект из предыдущей операции. Это недостаток в языке Java, потому что, если поддерживалось несколько возвращаемых значений, а streamи поддерживали их, у нас могли бы быть гораздо более простые нетривиальные задачи, выполняемые streamами.

До тех пор мало пользы.

EDIT 2018-02-12: Во время работы над проектом я написал вспомогательный class, который помогает обрабатывать особый случай наличия идентификатора ранее в streamе, который вам нужен в более позднее время, но часть streamа между ними не знает об этом. Пока я не выпущу его самостоятельно, он доступен на IdValue.java с модульным тестом на IdValueTest.java

Коллекции Eclipse имеют Pair и все комбинации примитивных / объектов Pairs (для всех восьми примитивов).

Завод Tuples может создавать экземпляры Pair , а фабрика PrimitiveTuples может использоваться для создания всех комбинаций пар примитивных / объектов.

Мы добавили их до того, как была выпущена Java 8. Они были полезны для реализации iteratorов ключевых / значений для наших примитивных карт, которые мы также поддерживаем во всех примитивных / объектных комбинациях.

Если вы захотите добавить дополнительные накладные расходы на библиотеку, вы можете использовать принятое решение Стюарта и собрать результаты в примитивный IntList чтобы избежать бокса. Мы добавили новые методы в Eclipse Collections 9.0, чтобы создавать коллекции Int/Long/Double из Int/Long/Double Streams.

 IntList list = IntLists.mutable.withAll(intStream); 

Примечание. Я являюсь коммиттером для коллекций Eclipse.

  • Рекурсивная функция хвоста для поиска глубины дерева в Ocaml
  • Почему java.util.Collection не реализует новый интерфейс Stream?
  • Почему функциональные языки?
  • какова хорошая постоянная структура коллекций для использования в java?
  • Функциональное программирование на Java
  • Поток Zipping с использованием JDK8 с lambda (java.util.stream.Streams.zip)
  • Зачем избегать подтипов?
  • Вычисление функции, которая принимает бесконечные аргументы
  • Группировать путем подсчета в Java 8 stream API
  • Переполнение стека из глубокой рекурсии в Java?
  • Каков способ Scala для реализации повторного вызова, подобного этому?
  • Давайте будем гением компьютера.