一起读源码1. Java 中元组 Tuple

Posted smile-yan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起读源码1. Java 中元组 Tuple相关的知识,希望对你有一定的参考价值。

1.1 问题描述

使用 Java 做数据分析、机器学习的时候,常常需要对批量的数据进行处理,如果需要处理的数据的维度不超过10时,可以考虑使用 org.javatuples 提供的 Tuple 类工具。支持 1 - 10 的多维数据处理,支持数据泛型。除了常见的 Integer / String / Long / Double 还可以是自定义的数据类型。

1.2 总体分析

如图所示,总共 13 个类,13个接口。

先看接口非常简单,附带自己的泛型 X,然后提供一个自己的方法 X getValue0() … X getValue9() … getValue … getKey … getLabel.

所有的类的父类为抽象类 Tuple,注意该方法并没有包含任何泛型,而是实现了常见的三个接口:

  • Iterable
  • Serializable
  • Comparable

接下来要出场的子类就根据实际情况实现多个接口,返回对应的值。具体内容如下表所示:

类名参数数目
Unit1
Pair2
KeyValue2
LabelValue2
Triplet3
Quartet4
Quintet5
Sextet6
Septet7
Octet8
Ennead9
Decade10

1.3 父类 Tuple

Tuple 是一个抽象类,具有两个成员变量,如下:

private final Object[] valueArray;
private final List<Object> valueList;

私有变量外部不能直接访问,这两个变量的主要作用应该是用于 Iterable 遍历。

接着看 Tuple 对这个接口的实现:

public final Iterator<Object> iterator() 
    return this.valueList.iterator();

比较容易理解,把 Tuple 的 iterator 转移给它内部变量完成。

同样地看一下 contains 方法,这个同样容易理解:如果待查变量为 null 则直接返回 false,否则逐个遍历看看是不是相等。

public final boolean contains(final Object value) 
    for (final Object val : this.valueList) 
        if (val == null) 
            if (value == null) 
                return true;
            
         else 
            if (val.equals(value)) 
                return true;
            
        
    
    return false;

接下来看一下其中的 indexOf 方法,也就是找到数据对应的索引:

public final int indexOf(final Object value) 
    int i = 0;
    for (final Object val : this.valueList) 
        if (val == null) 
            if (value == null) 
                return i;
            
         else 
            if (val.equals(value)) 
                return i;
            
        
        i++;
    
    return -1;

这个我认为不合理的地方就是最后的返回 -1 ,应该定义一个常量,NOT_FOUNT = -1 更加合适。其他的没有什么可以解释的。

类似地看一下 lastIndexOf ,这个只是倒过来遍历一次。

到目前为止还不知道成员变量 valueArray 的作用,看下源码发现,主要是用于Tuple转换为 Array 和 getValue 时使用(遍历链表时间复杂度为 O ( N ) O(N) O(N) 而此时根据索引读数据时间复杂度为 O ( 1 ) O(1) O(1))。

public final Object[] toArray() 
    return this.valueArray.clone();

public final Object getValue(final int pos) 
    if (pos >= getSize()) 
        throw new IllegalArgumentException(
                "Cannot retrieve position " + pos + " in " + this.getClass().getSimpleName() + 
                ". Positions for this class start with 0 and end with " + (getSize() - 1));
    
    return this.valueArray[pos];

1.4 子类以 Pair 为例

构造函数只提供了一个 ,即全参数构造函数

public Pair(
        final A value0, 
        final B value1) 
    super(value0, value1);
    this.val0 = value0;
    this.val1 = value1;

内部变量 final A value1 和 final B value2 是 final 类型的,因此如果希望更新它们的值不能直接 set 而是需要重新 new Pair,当然在 Pair 类中已经提供了。

public <X> Pair<X,B> setAt0(final X value) 
    return new Pair<X,B>(
            value, this.val1);


public <X> Pair<A,X> setAt1(final X value) 
    return new Pair<A,X>(
            this.val0, value);

而其他的方法比较有意思的是从 Pair 到 Triplet 等等更高维度的,都有提供这些方法。方法同样是 return new Triplet … 这类的。

另外还有经常使用的 with 方法创建Pair对象,比如 Pair.with(value1, value2);

对应的源码实现如下:

public static <A,B> Pair<A,B> with(final A value0, final B value1) 
    return new Pair<A,B>(value0,value1);

这些都是非常容易理解的内容,也可以借此学习一下 java 的泛型。

1.5 面向对象思维

所有这些类继承于一个父类 Tuple,该父类为抽象类,实现 Iterable 与 Comparable 接口

public abstract class Tuple implements Iterable<Object>, Serializable, Comparable<Tuple> 
    // 两个基本的数据结构
    private final Object[] valueArray;
    private final List<Object> valueList;
    
    ... 包括迭代器、遍历方法等

接下来的12个类均继承于 Tuple 类,并根据参数多少对相应的方法做了修改。

public final class Unit<A> extends Tuple implements IValue0<A> 


而 该接口也比较简单:

public interface IValue0<X> 
    public X getValue0();   

类似地,其他接口只是更改了方法的名字,以便于不同的类的使用。比如 Quartet 类有四个对象,则需要实现四个接口,分别实现 getValue0、getValue1、getValue2、getValue3 四个接口。

非常简单,但是也非常好用。

1.6 灵活应用

对于一个具有 m 个属性, 共 n 行的数据集,现在需要使用 List 和 Tuple 组合存储。

  1. 按行存储:存储后结果是 n 行,每一行是 一个 Tuple 对象,每个 Tuple 对象共包含 m 个属性。也就是说,先打包成行,再存储为一个 List。
  2. 按列存储:存储后结果是 m 列,然后把每一列对应的 List 对象存储为 Tuple。也就是说,先基于列进行打包,然后每一列对应一个 List 对象,然后使用 Tuple 进行合并。

这个应该要根据实际情况而定,一般情况下第二种情况可能更加多一些,基于属性进行打包,方便每次读取单个属性时返回一个 list。

1.7 总结

作为读源码的开端,特别选了一个简单的、也挺好用的。希望能够帮到大家~~

Smileyan
2022.7.28 19:56

以上是关于一起读源码1. Java 中元组 Tuple的主要内容,如果未能解决你的问题,请参考以下文章

一起读源码1. Java 中元组 Tuple

Python中元组常用的方法都有哪些,分别有啥作用?

C#中元组对象Tuple的使用

python中元组常识,以及for 与 range 的用法!

关于Python中元组的问题

Java中元组的使用