web-dev-qa-db-ja.com

Java SE 8にはペアまたはタプルがありますか?

私はJava SE 8で怠惰な関数操作で遊んでいます、そして私はペア/ Tuple (i, value[i])へのインデックスmap、そして次に2番目のvalue[i]要素に基づくifilterし、最後にインデックスだけを出力したいです。

JavaのC++ペア<L、R>に相当するものは何でしょうか? ラムダとストリームの大胆な新時代におけるものですか?

更新:以下の答えの1つで、@ dkatzelによって提供されたきちんとした解決策を持つ、かなり単純化された例を示しました。しかし、notgeneralizeはします。したがって、もっと一般的な例を追加しましょう。

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である3​​つの列のcountsに対応します。必要なのは、これら3つの列のindicesです。正しい出力は[0, 2, 4]です。どうすればこの結果が得られますか?

163
necromancer

UPDATE:この回答は元の質問に対する回答ですJava SE 8にペアまたはタプルはありますか?(そして暗黙のうちにそうではないのですか?)OPは質問をより完全な例で更新しましたが、Pair構造を使用せずに解決できるようです。 [OPからのメモ:これが もう片方の正解 です。]


短い答えはノーです。自分でロールするか、それを実装しているいくつかのライブラリのいずれかを持ってくる必要があります。

Java SEでPairクラスを持つことが提案され、少なくとも一度は拒否されました。 OpenJDKメーリングリストの/上の このディスカッションスレッド を参照してください。トレードオフは明白ではありません。一方では、他のライブラリやアプリケーションコードには多くのPair実装があります。それは必要性を示しており、そのようなクラスをJava SEに追加すると再利用と共有が増えるでしょう。一方、Pairクラスを持つと、必要な型や抽象化を作成せずに、Pairsやコレクションから複雑なデータ構造を作成したくなります。 (これは Kevin Bourillionのメッセージ からの言い換えです。)

私は皆がその全体の電子メールスレッドを読むことを勧めます。それは非常に洞察力に富んでいて、そして燃え尽きることはありません。非常に説得力があります。それが始まったとき、私は「ええ、Java SEにはPairクラスがあるべきだ」と思いましたが、スレッドが終わりに達するまでに私は考えを変えました。

ただし、JavaFXには javafx.util.Pair クラスがあります。 JavaFXのAPIは、Java SE APIとは別に進化しました。

リンクされた質問からわかるように、 JavaのC++ペアと同等のものは何ですか? そのような単純なAPIのようなものを囲む、かなり大きな設計スペースがあります。オブジェクトは不変であるべきですか?それらは直列化可能であるべきですか?それらは同等であるべきですか?クラスは最終的なものかどうか2つの要素を並べるべきですか?それはインターフェースかクラスか?なぜペアでやめるのですか?トリプル、クワッド、またはNタプルではないのはなぜですか。

そしてもちろん、要素には避けられない命名規則があります。

  • (a、b)
  • (1番目、2番目)
  • (左右)
  • (車、cdr)
  • (foo、bar)
  • 等.

ほとんど言及されていない1つの大きな問題は、ペアとプリミティブの関係です。 2D空間内の点を表す(int x, int y)データがある場合、これをPair<Integer, Integer>として表すと、2つの32ビットワードの代わりにthree objectsが消費されます。さらに、これらのオブジェクトはヒープ上に存在しなければならず、GCオーバーヘッドが発生します。

Streamsのように、Pairsの基本的な特殊化があることが不可欠であることは明らかに思われます。見たいですか?

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

IntIntPairでも、ヒープ上に1つのオブジェクトが必要です。

これらは、もちろん、Java SE 8のJava.util.functionパッケージにおける機能的インタフェースの急増を彷彿とさせます。肥大化したAPIが不要な場合は、どれを除外しますか?これだけでは不十分で、たとえばBooleanの特殊化も追加する必要があると考えることもできます。

私が考えているのは、JavaがPairクラスをずっと前に追加していたとすれば、それが単純なもの、あるいは単純化されたものであったことでしょう。もしペアがJDK 1.0の時間枠に追加されていたら、それはおそらく変更可能だったでしょう! (Java.util.Dateを見てください。)人々はそれに満足していますか?私は、JavaにPairクラスがあったとしても、それはちょっとそんなに便利ではなく、みんなが自分たちのニーズを満たすために自分自身を転がしていくことになると思います。それでも人々は、JavaのPairクラスを修正する方法について議論しているでしょう。言い換えれば、私たちが今日いるのと同じ場所のようなものです。

その間、値型のためのJVM(そして最終的にはJava言語)でのより良いサポートであるという根本的な問題に取り組むための作業がいくつか進行中です。これを参照してください 値の状態 ドキュメント。これは暫定的な投機的な作業であり、JVMの観点からの問題だけを網羅していますが、その背後にはすでにかなりの考察があります。もちろん、これがJava 9に取り込まれる、またはどこにでも取り込まれるという保証はありませんが、このトピックに関する現在の考え方の方向を示しています。

182
Stuart Marks

これらの組み込みクラスを見てみることができます。

32
senerh

残念ながら、Java 8はペアやタプルを導入しませんでした。あなたはいつでも org.Apache.commons.lang3.Tuple を使うことができます(私は個人的に私はJava 8と組み合わせて使います)またはあなたが作成することができます独自のラッパーまたは地図を使用してください。またはそのようなものは、あなたがリンクしたその質問に対する 受け入れられた答え で説明されているように。

17
blalasaadri

この完全な例は、いかなる種類のPair構造を使用せずにも解決できると思われます。重要なのは、列インデックスをその列の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によって要求された正しい結果です。

また、int値をIntegerオブジェクトにボックス化するboxed()操作にも注意してください。これにより、ボクシングを行うコレクター関数を書く必要がなく、既存のtoList()コレクターを使用することができます。

14
Stuart Marks

あなたはインデックスを気にするだけなので、タプルにマッピングする必要はまったくありません。配列のルックアップ要素を使用するフィルタを作成しないのはなぜですか。

     int[] value =  ...


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

はい。

Map.EntryPairとして使用できます。

残念ながら、ラムダが複数の引数を取ることができるとしても、Java言語は単一の値(オブジェクト型またはプリミティブ型)を返すことしかできないため、Java 8ストリームには役立ちません。これはあなたがストリームを持っている時はいつでもあなたが前の操作から単一のオブジェクトを渡されることになってしまうことを意味します。複数の戻り値がサポートされていて、ストリームがそれらをサポートしている場合、ストリームによって行われる非常に優れた自明ではないタスクがある可能性があるため、これはJava言語の欠如です。

それまでは、ほとんど役に立ちません。

編集2018年2月12日:プロジェクトに取り組んでいる間私はあなたが後で必要なストリームの早い方で識別子を持つという特別な場合を扱うのを助けるヘルパークラスを書きましたが間のストリーム部分はそれについて知りません。それを自分でリリースするまでは、 IdValue.Java で入手可能です。単体テストは IdValueTestにあります。 .Java

Vavr(以前のJavaslang)http://www.vavr.io )はタプルを提供します( 8)のサイズまで。これがjavadocです。 https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html

これは簡単な例です。

Tuple2<Integer, String> entry = Tuple.of(1, "A");

Integer key = entry._1;
String value = entry._2;

なぜJDK自体が今まで単純な種類のタプルを持っていなかったのかは私にとって謎です。ラッパークラスを書くのは日常の業務のようです。

2
wumpz

Eclipse CollectionsにはPairと(8つすべてのプリミティブのための)プリミティブ/オブジェクトペアのすべての組み合わせがあります。

Tuples ファクトリーはPairのインスタンスを作成でき、 PrimitiveTuples ファクトリーは作成できます。プリミティブ/オブジェクトペアのすべての組み合わせを作成するために使用されます。

Java 8がリリースされる前にこれらを追加しました。それらは私達のプリミティブマップのためにキー/値イテレーターを実装するのに役立ちました。そして私達は全てのプリミティブ/オブジェクトの組み合わせでそれをサポートします。

追加のライブラリオーバーヘッドを追加しても構わない場合は、Stuartの承認済みソリューションを使用して、結果をボクシングを回避するためのプリミティブIntListに収集できます。 Int/Long/DoubleコレクションをInt/Long/Double Streamsから作成できるように、 Eclipse Collections 9.0 に新しいメソッドを追加しました。

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

注:私はEclipseコレクションのコミッターです。

2
Donald Raab

Java 9以降、以前よりも簡単にMap.Entryのインスタンスを作成できます。

Entry<Integer, String> pair = Map.entry(1, "a");

Map.entry は変更不可能なEntryを返し、nullを禁止します。

1
ZhekaKozlov

私はJavaの専門家ではなく、単なる学生ですが、Pythonのようなタプルをシミュレートするための回避策を作成しました。

class C1 { 
    String name; int X,Y;
    C1(List l){name = ""+l.get(0); X=(int)l.get(1); Y=(int)l.get(2);}
}

class MyClass{
    List l2= List.of(List.of("a",7,9), List.of("b",8,4), List.of("c",3,6)); //here
    List l3=new ArrayList();
    for (Object li: l2) l3.add(new C1((List)li));
}

お役に立てれば。

0
Carlos Leandro