Java元组

javatuples


问题

有时你会有类似如下的需求:

1逻辑上,方法需要返回两个甚至更多的值(通常它们的类型并不一致,因此无法使用数组)。形如:

return "tom", 18; // 有些语言支持这种语法,但是 Java 并没有这个语法特性。



2有时,你需要向方法传入类似于多个学生的姓名和年龄:

demo("tom", 19, "jerry", 18, ...) // 当然这就是在使用不定参/可变参语法



对于上述的情况,你可以采用这样的解决办法:

1将多个数据封装到一个 Map 中,或者定义一个类(例如 Student),将数据封装到对象中:

map.put("name", "tom");
map.put("age", 18);
return map;

return new Student("name", 18);



2对于第二个问题,可以使用数组

names[0] = "tom";
ages[0] = 19;

names[1] = "jerry";
ages[1] = 18;

demo(names, ages);



不过,在使用上述方案实现相关需求后,你可能会有如下相反:

1使用 Map 或自定义类有点【杀鸡用牛刀】;
2使用两个数组的时候,将一个人的两个信息分开存放,感觉又有点怪怪的。


tuples


数据结构领域中有一个较少提及的数据结构:【元组】(tuple) 。有些语言中,天生既有 tuple 类型的变量,但是 Java 中没有(其实,常见的编程语言中天生没有它的占大多)。

tuple 结构和数组很类似,它作为一个容器其中可以存放多个值,而不要求这些值的类型必须一致。于此同时,它和数组一样有下标索引的概念,可以通过下标索引从中取值。

当然,我们可以自己实现 tuple 数据结果(相较于 List、Set 它其实简单很多)。不过,很显然有现成的:javatuples

<!-- https://mvnrepository.com/artifact/org.javatuples/javatuples -->
<dependency>
    <groupId>org.javatuples</groupId>
    <artifactId>javatuples</artifactId>
    <version>1.2</version>
</dependency>



由于 tuple 数据结构的功能/作用实在是比较简单(有时可能项目中就有程序员自己随手就实现了它),所以这个库,在 2011 年就【彻底】完成了所有功能,最后一个版本就是 1.2 。

The tuple classes

该工具库提供了以下不同容量的 tuple 类:

容量

Unit<A>

1 element

Pair<A,B>

2 elements

Triplet<A,B,C>

3 elements

Quartet<A,B,C,D>

4 elements

Quintet<A,B,C,D,E>

5 elements

Sextet<A,B,C,D,E,F>

6 elements

Septet<A,B,C,D,E,F,G>

7 elements

Octet<A,B,C,D,E,F,G,H>

8 elements

Ennead<A,B,C,D,E,F,G,H,I>

9 elements

Decade<A,B,C,D,E,F,G,H,I,J>

10 elements


我也是很服气作者能为每一种容量的 tuple 类都单独起了个名字!

由于上述 tuple 类并没有什么语义,所以,作者额外地为两个常见的情况提供了单独的 tuple 类。实际上,它们俩就是 Pair 的别名。

●KeyValue<A,B>
●LabelValue<A,B>


Creating tuples



所有类型的 tuple 都可以通过 new 来创建:

Pair<Integer, Integer> pair = new Pair<>(10, 20);

Triplet<String, Integer, Date> triplet = new Triplet<>("hello", 10, new Date());

...




于此同时,tuple 还提供了静态方法 .with() 来创建各种 ruple 类的实例:
 

Pair<Integer, Integer> pair = new Pair<>(10, 20);

Triplet<String, Integer, Date> triplet = new Triplet<>("hello", 10, new Date());

...


Getting/Setting values



tuple 数据结构在概念上是下标索引的,但是你不能想当然地对其使用下标运算符 [] 。

从一个 tuple 容器中取值,有两种方式:

1通过 .getValueN() 方法:

System.out.println( pair.getValue0() );
System.out.println( pair.getValue1() );

System.out.println( triplet.getValue0() );
System.out.println( triplet.getValue1() );
System.out.println( triplet.getValue2() );


2通过 .getValue(index) 方法:

System.out.println( pair.getValue(0) );
System.out.println( pair.getValue(1) );

System.out.println( triplet.getValue(0) );
System.out.println( triplet.getValue(1) );
System.out.println( triplet.getValue(2) );


不过 .getValue(index) 方法取出来的值统一都是 Object 类型,后续使用时需要做类型转换。

优先考虑使用 .getValueN() 方法 。

另外,大家都能猜到,既然有 get 方法,这里也自然有 set 方法:
 

pair.setAt0(xxx);
pair.setAt1(xxx);

triplet.setAt0(xxx);
triplet.setAt1(xxx);
triplet.setAt2(xxx);


KeyValue and LabelValue 这两种【额外】的 tuple 类型中,它们的 getting/setting 方法是叫:getKey() / getValue() 和 getLabel() / getValue() 。



Adding or removing elements



当你向一个 Pair 对象中添加元素时,你将获得一个 Triplet 对象;当你从一个 Triplet 对象中移除一个元素时,你将获得一个 Pair 对象。

也就是说,任何一种 tuple 类的容量是不可改变的。

Pair<Integer, Integer> pair = Pair.with(10, 20);

Triplet<Integer, Integer, Integer> triplet = pair.add(30);

System.out.println( triplet );  // 10, 20, 30




调用 .add() 方法时,添加的元素将被添加到末尾。

另外,tuple 还提供 .addAtN() 方法。将要添加的元素添加到指定位置,而原位置(及后续内容)依次后移。

triplet = pair.addAt1(30);

System.out.println( triplet );  // 10, 30, 20




从 tuple 中移除元素使用 .removeFromN() 方法。

pair = triplet.removeFrom0();


Converting to/from collections or arrays



任何一种 tuple 都可以转换成 List 或数组:

Java复制代码

Object[] array = triplet.toArray();
List<Object> list = triplet.toList();


反向的操作有:

Java复制代码

String[] array = ...
...
Quartet<String,String,String,String> quartet = Quartet.fromArray(array);


这里需要注意的是,由于 Array 和 List 是要求其中元素类型是一致的。所以,从 tuple 转成数组和 List 时,会失去元素的具体类型,从而得到一个 Object 的数组和 List 。如果这样处理,那么就失去了 tuple 的使用价值。

同样,对于一个一致的某种类型的数组和 List,转换成 tuple 时,其元素类型必然也都是一样的,这样也就没有必要去使用 tuple 了,为什么不直接使用这个数组和 List 呢。



Iterating



由于所有类型的 tuple 都是【可循环】对象,所以可以直接用便捷 for 循环对其进行遍历:

Java复制代码

for (Object value : triplet) {
    ...
}


不过,这里失去了每个元素的具体类型,只能将它们统一当作 Object 来看待。



Checking contents



tuple 提供了方法用来判断 tuple 中是否包含某个/某些对象:

Java复制代码

if (quartet.contains(value)) {
    ...
}

if (quartet.containsAll(valueCollection)) {
    ...
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值