📜 ⬆️ ⬇️

Object tuples in Java and their collections

Quite often in practice it is necessary to use such constructions as “pair” ( Pair<First, Second> ), less often “trinar” objects ( Triplet<First, Second, Third> ) and longer chains of associated objects. In this connection, I always wondered why in the JDK (in java.lang.* Or java.util.* ) Until now there is no standard Pair<First, Second> or longer structures. I think many Java programmers have their own pair implementation. I am not an exception.

And once again stumbling upon the need to work with a large number of different tuples, I decided to tackle this problem systematically. He came up with the name of the project, determined the goals and after the experiments that took some time, laid out the code (git: //github.com/telesik/rumba.git).

What I wanted to achieve:

Appearance of a tuple


And now the Cortege interface was 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> { } } 

The figure below clearly shows the mechanism for describing tuples of arbitrary length and element types.
image
Fig. 1. The mechanism for describing tuples of arbitrary length and element types
')
But what is a “bare” interface if there is not a single implementation? There is such! Her name is CortegeChain . The presence of the word Chain in the title is due to the way in which the elements in the tuple are stored. I assume and hope that in the future there will be other implementations optimized for various types of use.
In the implementation of CortegeChain I did not set specific optimization goals in terms of memory usage or speed. The main task that I wanted to solve was to try the idea itself, to find weak points, to identify the obvious and not obvious opportunities opening up for the developer.

Examples of using


I will go directly to the illustrative examples of use:

 //   : Cortege<Long, Cortege<String, Cortege.End>> cortegeLS = CortegeChain.create(2); 

Attention should be paid to the argument of the create method. Due to the fact that in Java, generics exist only at the compilation stage, and I didn’t find any “legitimate” methods at runtime :(, I had to pay for it at such a price. The essence of this parameter is the depth declaration of the created tuple. But since the developer usually knows the number of elements, the need to specify this parameter should not cause big problems.

Note: But still, if someone can offer their solution to this problem with the parameter - I will be extremely grateful.

So an example
 //    // 1-  (    ,   ) cortegeLS.setValue(4L); cortegeLS.nextElement().setValue("str"); // 2-  (  ,   ) cortegeLS.setValue(4L).setValue("str"); // 3-  ( ,   ) cortegeLS.setValues(4L, "str"); 

Now reading
 // 1-  (    ,   ) Long valueA = cortegeLS.getValue(); // 2-  (    ,   ) String valueB = cortegeLS.nextElement().getValue(); // 3-  (    ,   ) Long valueC = cortegeLS.getValue(1); String valueD = cortegeLS.getValue(2); 

What else can be done with a tuple:
1. Get the tuple “right”.
 Cortege<String, Cortege.End> rightCortegeS = cortegeLS.nextElement(); //   Cortege.End rightCortegeEnd = cortegeLS.nextElement().nextElement(); 

2. Get the “depth” (number of elements in the tuple)
 int deep = cortegeLS.getDeep(); 

Perhaps all. But not all! :)

Tuple collections


I created a tuple for a reason. Now having such a construction it would be nice to “tie” it to a friendly collection family ( java.util.Collection ). No sooner said than done. But the banal use of a tuple as an element of the collection is not interesting. It would be desirable not only to find, delete, modify elements of the collection as a whole, which is given by traditional implementations of the interfaces java.util.Set and java.util.List , but also to search, filter by individual elements of the tuples stored in the collection, modify the entire columns, etc. that is natural for relational tables.

So again setting goals:

Note: From all that I wanted, it didn’t work out only to implement the last item “view” (analogue of view in relational databases). But the work is going on and, perhaps in the near future, the realization of this promising idea will be born.

And that's what happened:
 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> { } 


image
Fig. 2. Topology of tuple collections

Examples of using tuple collections


Now, probably, as in the case of the description of Cortege it is Cortege to proceed immediately to the illustrative examples of use:
 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"); } } 


Let's sum up


As you can see from the example, it turned out to be a fairly convenient library for storing and manipulating objects with long tuples. The benefits of the library include:

In addition to the Cortege structure itself, there are two types of collections in the library, extending the well-known java.util.Set and java.util.List (respectively, com.rumba.cortege.CortegeSet and com.rumba.cortege.CortegeList ).

But there are obvious shortcomings due primarily to the inability to “get” to the generics declarations in Java and, as a result, the loss of control over the type when referring to the element of the tuple. For the sake of justice, I looked for libraries that solve such problems, and found a rather interesting implementation of javatuples . In it, the problem of declaring “long” chains of associated objects is solved by simply going over the “reasonable” from the point of view of the developers of the maximum tuple size.


Conclusion


Working on the library, I tried to cover the most typical, and from my point of view, useful tasks that arise when working with tuples and their collections. Unfortunately, not all plans are implemented, but I hope that a bit of work that was done will be useful to someone. But the work continues, I hope not in vain.
It would be very interesting to get a constructive fitback for this article and library.

useful links


javatuples
quite an interesting attempt to implement

Source: https://habr.com/ru/post/144176/


All Articles