Pair<First, Second>
)、それほど頻繁ではない「トリナー」オブジェクト(
Triplet<First, Second, Third>
)および関連オブジェクトの長いチェーンなどの構造を使用する必要があります。 これに関連して、私は常にJDK(
java.lang.*
または
java.util.*
)でなぜ標準的な
Pair<First, Second>
またはそれ以上の構造が存在しないのか疑問に思っていました。 多くのJavaプログラマーが独自の「ペア」実装を持っていると思います。 私も例外ではありません。
そして、多数の異なるタプルを使用する必要性に再び遭遇し、この問題に体系的に取り組むことにしました。 彼はプロジェクトの名前を思いつき、目標を設定し、時間のかかる実験の後、コードを投稿しました(git://github.com/telesik/rumba.git)。
私が達成したかったもの:
- 結果のタプルの位置からタイプが決定されるいくつかのオブジェクトの関連付けには、便利な構造が必要です( タプルと呼びましょう)。
- タプルの長さとタイプは無制限でなければなりません。
- 要素タイプは比較的制御する必要があります(少なくともジェネリックに制限されます)。
- タプルの構造の説明は単純でなければなりません。
タプルの光の現象
そして、ここで
Cortege
インターフェース
Cortege
:
public interface Cortege<V, T extends Cortege> { T setValue(V value); T nextElement(); <Vi> Vi getValue(int index) throws ClassCastException; // be careful with types. Type is not bounded int getDeep(); <Vi> void setValue(int index, Vi value); // be careful with types. Type is not bounded Cortege<V, T> setValues(Object... values); // be careful with types. Type is not bounded!!! V getValue(); static abstract class End<V, T extends Cortege<V, T>> implements Cortege<V, T> { } }
以下の図は、任意の長さと要素タイプのタプルを記述するためのメカニズムを明確に示しています
図 1.任意の長さのタプルと要素のタイプを記述するメカニズム
しかし、実装がない場合の裸のインターフェースは何ですか? あります! 彼女の名前は
CortegeChain
です。 名前にチェーンという単語が存在するのは、タプル内の要素のストレージを整理する方法によるものです。 将来的には、さまざまなタイプの使用に最適化された他の実装が存在することを期待しています。
CortegeChain
の実装では、メモリ使用量または速度に関して特定の最適化目標を設定しませんでした。 私が解決したかった主なタスクは、アイデアそのものを試すこと、弱点を見つけること、開発者に開かれた明白な機会と明白でない機会を特定することでした。
使用例
すぐに使用例を説明します。
// : Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(2);
createメソッドの引数に注意してください。 Javaジェネリックはコンパイル段階でのみ存在し、実行時にそれらに到達するための「正当な」メソッドを見つけられなかったという事実のために:(、私はそのような価格でそれを「支払う」必要がありました。このパラメーターの本質は作成されたタプルの深さの宣言です。開発者は通常要素の数を知っているので、このパラメーターを指定する必要は大きな問題にはなりません。
注:それでも、誰かがソリューションを提供できる場合、これはパラメーターの問題です。非常に感謝します。
ので、例
// // 1- ( , ) cortegeLS.setValue(4L); cortegeLS.nextElement().setValue("str"); // 2- ( , ) cortegeLS.setValue(4L).setValue("str"); // 3- ( , ) cortegeLS.setValues(4L, "str");
今読んでいる
// 1- ( , ) Long valueA = cortegeLS.getValue(); // 2- ( , ) String valueB = cortegeLS.nextElement().getValue(); // 3- ( , ) Long valueC = cortegeLS.getValue(1); String valueD = cortegeLS.getValue(2);
タプルで他にできること:
1.タプルを「右側」に取得します。
Cortege<String, Cortege.End> rightCortegeS = cortegeLS.nextElement(); // Cortege.End rightCortegeEnd = cortegeLS.nextElement().nextElement();
2.「深さ」(タプルの要素数)を取得します
int deep = cortegeLS.getDeep();
おそらくそれだけです。 しかし、すべてではありません! :)
タプルコレクション
理由でタプルを作成しました。 さて、このような構造を持っているので、友好的なコレクションファミリ(
java.util.Collection
)に「結び付ける」のがいいでしょう。 すぐに言ってやった。 しかし、タプルをコレクションの要素として平凡に使用することは面白くありません。
java.util.Set
および
java.util.List
インターフェイスの従来の実装を提供するコレクション全体の要素を検索、削除、変更するだけでなく、コレクションに格納されているタプルの個々の要素による検索、フィルタリング、列全体の変更なども行いたいです。これはリレーショナルテーブルにとっては自然なことです。
再び目標を設定します。
- コレクションは、古くから確立されている
java.util.Collection
ファミリーのインターフェース(java.util.Set
やjava.util.List
)と互換性がある必要があります。 - 実装は、単純なオブジェクト(たとえば、
java.util.HashSet<T>
)の世界で類似物の機能を拡張する必要があります。 - コレクションは抽象的な基準で簡単に検索できる必要があります。
- サブセットを実装する必要があります。
- コレクション上にビュー(リレーショナルデータベースのビューのアナログ)を作成する可能性を認識したいと思います。
- ビューはソースデータを「所有」せず、基準に従って元のコレクションのみをフィルタリングします
- フィルタリングは使い捨てではなく、動的でなければなりません。 つまり 関連性があり、元のテーブルの状態を反映する必要があります。 コレクションへのすべての挿入、変更、および削除は、 viewに重畳された述語に従って、その表現に反映される必要があります 。
注:私が望んでいたすべての中で、最後の項目「ビュー」(リレーショナルデータベースのビューのアナログ)のみを実装することはできませんでした。 しかし、作業は進行中であり、おそらく近い将来、この有望なアイデアの実現が世界にもたらされるでしょう。
そして、ここで何が起こったのです:
public interface CortegeCollection<T extends Cortege> extends Collection<T>, Iterable<T> { <T> boolean contains(int num, T obj); CortegeCollection<T> extract(int num, Object key); CortegeCollection<T> extract(Corteges.Predicate<T> predicate); // <C> CortegeCollection<T> view(int num, Corteges.Predicate<C> predicate); T findAny(int num, Object key); T findAny(Corteges.Predicate<T> predicate); <Vi> List<Vi> getColumnCopy(int num); <Vi> void fill(int num, Vi value); } public interface CortegeSet<T extends Cortege> extends CortegeCollection<T>, Set<T> { } public interface CortegeList<T extends Cortege> extends CortegeCollection<T>, List<T> { }
図 2.タプルコレクションのトポロジ
タプルコレクションの使用例
Cortege
説明の場合のように、今ではおそらく価値があります
Cortege
すぐに視覚的な使用例に進みます。
public class ExampleCollections { public static void main(String[] args) { // CortegeHashSet CortegeSet<Cortege<Long, Cortege<String, Cortege.End>>> cortegeHashSetLS = Corteges.newCortegeHashSet(2); for (long i = 0; i < 5; i++) { Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(2); cortegeLS.setValue(i).setValue("" + i); cortegeHashSetLS.add(cortegeLS); } for (Cortege cortege : cortegeHashSetLS) { System.out.println(cortege); } cortegeHashSetLS.add(CortegeChain.<Long, Cortege<String, Cortege.End>>create(2)); Cortege<Long, Cortege<String, Cortege.End>> cortegeIS = CortegeChain.create(2); System.out.println(cortegeHashSetLS.contains(cortegeIS)); cortegeIS.setValue(null).setValue("3"); System.out.println(cortegeIS); System.out.println(cortegeHashSetLS.contains(cortegeIS)); System.out.println(cortegeHashSetLS.contains(1, 3L)); Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortegeLLS1 = CortegeChain.create(3); Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortegeLLS2 = CortegeChain.create(3); Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortegeLLS3 = CortegeChain.create(3); CortegeChain<String, CortegeChain<Long, CortegeChain<String, Cortege.End>>> cortegeSLS = CortegeChain.create(3); cortegeLLS1.setValue(1L); cortegeLLS1.nextElement().setValue(11L); cortegeLLS1.nextElement().nextElement().setValue("AAA"); cortegeLLS2.setValue(2L); cortegeLLS2.nextElement().nextElement().setValue("BBB"); cortegeLLS3.setValue(3L); cortegeLLS3.nextElement().setValue(33L); cortegeLLS3.nextElement().nextElement().setValue("AAA"); CortegeHashSet<Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>>> cortegeSetLLS = Corteges.newCortegeHashSet(cortegeLLS1.getDeep()); System.out.println(cortegeSetLLS.contains(cortegeLLS1)); cortegeSetLLS.add(cortegeLLS1); cortegeSetLLS.add(cortegeLLS2); cortegeSetLLS.add(cortegeLLS3); System.out.println(cortegeSetLLS.contains(cortegeLLS1)); for (Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortege : cortegeSetLLS) { System.out.println(cortege); } System.out.println(cortegeSetLLS.contains(3, "AAA")); cortegeSetLLS.fill(1, 5L); cortegeSetLLS.fill(2, 8L); cortegeSetLLS.fill(3, "XXX"); for (Cortege<Long, Cortege<Long, Cortege<String, Cortege.End>>> cortege : cortegeSetLLS) { System.out.println(cortege); } // Collection<Cortege> corteges = cortegeSetLLS.extract(2, "111"); } }
まとめると
この例からわかるように、長いタプルのオブジェクトを格納および操作するための非常に便利なライブラリが得られました。 ライブラリの利点は次のとおりです。
- 任意の長さのタプルを作成するのは非常に簡単で、タプルの将来のインスタンスを構成する要素のタイプをリストするだけです。
- タプルの要素に順次アクセスする場合、要素のタイプが制御されます
- タプルの宣言と格納の選択された構造により、「右側」の
nextElement()
を取得することが可能です(nextElement()
メソッド)
ライブラリには、Cortege構造自体に加えて、よく知られている
java.util.Set
および
java.util.List
を拡張する2種類のコレクションがあり
com.rumba.cortege.CortegeList
com.rumba.cortege.CortegeSet
および
com.rumba.cortege.CortegeList
)。
- 要素が参照オブジェクト(extract(int num、Object key)およびfindAny(int num、Object key)メソッド)と同等であるという単純な基準によって、コレクション内のタプルまたはタプルのサブセットを見つけることができます。
- 述語(extract(Corteges.Predicate <T>述語)およびfindAny(Corteges.Predicate <T>述語)メソッド)によってコレクション内のタプルまたはタプルのサブセットを見つけることができます。
- コレクションのタプルの要素から形成された指定された数の列を取得することが可能です
- 要素の列全体を埋めることができます
しかし、主にJavaのジェネリック宣言を「取得」できないことによって引き起こされる明らかな欠点があり、その結果、インデックスによってタプル要素にアクセスするときに型制御が失われます。 公平に、私は同様の問題を解決するライブラリを探し、 javatuplesのかなり興味深い実装を見つけました。 その中で、関連オブジェクトの「長い」チェーンを宣言する問題は、開発者の観点から「合理的な」タプルの単純な列挙によって解決されます。
- ユニット<A>(1要素)
- ペア<A、B>(2要素)
- トリプレット<A、B、C>(3要素)
- カルテット<A、B、C、D>(4要素)
- クインテット<A、B、C、D、E>(5要素)
- Sextet <A、B、C、D、E、F>(6要素)
- セプテット<A、B、C、D、E、F、G>(7要素)
- オクテット<A、B、C、D、E、F、G、H>(8要素)
- エニード<A、B、C、D、E、F、G、H、I>(9要素)
- 10年<A、B、C、D、E、F、G、H、I、J>(10要素)
おわりに
ライブラリでの作業中に、タプルとそのコレクションを操作するときに発生する最も典型的な、そして私の観点から、有用なタスクをカバーしようとしました。 残念ながら、すべての計画が実装されているわけではありませんが、完了した作業の一部が誰かに役立つことを願っています。 しかし、作業は継続され、無駄にならないことを願っています。
この記事とライブラリの建設的な適合を取得することは非常に興味深いでしょう。
便利なリンク
javatuples
実装する非常に興味深い試み