`

A data matrix implementation 1

    博客分类:
  • OO*
阅读更多
Though, as I wrote before, it's very hard to come up a universal dynamic data structure to fit all needs, sometimes we still need a somewhat general dynamic data structure due to requirements. For example, the spreadsheet could have as many columns during runtime, we can't determine the number of columns during compile time.

I have a very bad example of an implementation at work, it has some 3000 lines of code. I think it's for me to think about how it ends up like this and what are the better alternatives. Another motivation is still from the blog, I want to see how close I can get to ultimate - universal dynamic data structure, though I don't believe we can reach there. But the old saying says, you can't reach the North Star, but it always points to the right direction.

So here is my journey.

It's a data matrix, it has headers that we call fields, it has rows and columns. Each column has same data type.  This class is going across wires as well. And it has 3000 lines, for God's sake.

  • stripped out the code for memory allocation, replace them with Collection classes
  • stripped out the type meta data, removed all the type-safe getters/setters, they should be on the caller side, the meta data should be in the headers
  • stripped out the methods related to cell/row/column computations. This should be a dedicated class later on since the same kind of logic spreads in a few places.
  • stripped out the toXml() and other output methods.

After consolidate method signatures, The new class is like this

java 代码
 
  1. import java.util.List;  
  2. import java.util.Map;  
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5. /** 
  6.  * A Data matrix class, mainly for weak typed data, e.g., a table in swing. 
  7.  * Implementation reference, TableModel, DefaultTableModel. 
  8.  * 
  9.  * Note that the header type Field is really used as a flag, except the 
  10.  * hashcode used in the Map class. 
  11.  */  
  12. public class DataMatrix  
  13. {  
  14.     private List columns; // column values  
  15.     private List fields;  // field values, order is significant.  
  16.   
  17.     private Map fieldToColumns; // what to do with duplicated fields.  
  18.   
  19.     public DataMatrix()  
  20.     {  
  21.         fields = new ArrayList();  
  22.         columns = new ArrayList();  
  23.         fieldToColumns = new HashMap();  
  24.     }  
  25.   
  26.     public DataMatrix(List fields)  
  27.     {  
  28.         this.fields = new ArrayList(fields);  
  29.         columns = new ArrayList(fields.size());  
  30.         fieldToColumns = new HashMap(fields.size());  
  31.     }  
  32.   
  33.     public DataMatrix(DataMatrix dataMatrix)  
  34.     {  
  35.         this.columns = new ArrayList(dataMatrix.columns);  
  36.         this.fields = new ArrayList(dataMatrix.fields);  
  37.         this.fieldToColumns = new HashMap(dataMatrix.fieldToColumns);  
  38.     }  
  39.   
  40.     //-----------------------------------------------------------------------  
  41.     // field operations  
  42.     //-----------------------------------------------------------------------  
  43.     public List getColumnFields() { return fields; }  
  44.   
  45.     public void setColumnFields(List fields) { this.fields = new ArrayList(fields); }  
  46.   
  47.     public Field getField(int col) { return (Field)fields.get(col); }  
  48.   
  49.     public int getColumnNumber(Field field)  
  50.     {  
  51.         Integer index = (Integer)fieldToColumns.get(field);  
  52.         // should throw a cumstomized exception  
  53.         if (index == nullthrow new IllegalArgumentException("field not found: " + field.toString());  
  54.   
  55.         return index.intValue();  
  56.     }  
  57.   
  58.     //-----------------------------------------------------------------------  
  59.     // column operations  
  60.     //-----------------------------------------------------------------------  
  61.     public int getNumberOfColumns() { return columns.size(); }  
  62.   
  63.     public List getColumn(int col) { return (List) columns.get(col); }  
  64.   
  65.     public List getColumn(Field key)  
  66.     {  
  67.         return (List) columns.get(getColumnNumber(key));  
  68.     }  
  69.   
  70.     public void addColumn(Field field, List column)  
  71.     {  
  72.         checkNullAndEqualSize(field, column);  
  73.   
  74.         fieldToColumns.put(field, new Integer(fields.size()));  
  75.         fields.add(field);  
  76.         columns.add(new ArrayList(column));  
  77.     }  
  78.   
  79.     public void insertColumn(int position, Field field, List column)  
  80.     {  
  81.         checkNullAndEqualSize(field, column);  
  82.   
  83.         fieldToColumns.put(field, new Integer(position));  
  84.         fields.add(position, field);  
  85.         columns.add(position, column);  
  86.     }  
  87.   
  88.     private void checkNullAndEqualSize(Field field, List column)  
  89.     {  
  90.         if (field == nullthrow new IllegalArgumentException("field is null");  
  91.   
  92.         if (column == nullthrow new IllegalArgumentException("column is null");  
  93.   
  94.         if (columns.size() > 0)  
  95.         {  
  96.             List existingRow = (List)columns.get(0);  
  97.             if (existingRow.size() != column.size())  
  98.             {  
  99.                 throw new IllegalArgumentException("column size doesn't match: "  
  100.                         + " existing column size=" + existingRow.size()  
  101.                         + " to be added column size=" + column.size());  
  102.             }  
  103.         }  
  104.     }  
  105.   
  106.     public void deleteColumn(int position)  
  107.     {  
  108.         fieldToColumns.remove(fields.get(position));  
  109.         fields.remove(position);  
  110.         columns.remove(position);  
  111.     }  
  112.   
  113.     public void deleteColumn(Field field)  
  114.     {  
  115.         Integer index = (Integer)fieldToColumns.get(field);  
  116.         if (index == nullreturn// no such field here, do nothing  
  117.   
  118.         fieldToColumns.remove(field);  
  119.         fields.remove(index.intValue());  
  120.         columns.remove(index.intValue());  
  121.     }  
  122.   
  123.     // reorder the data matrix with the given fields.  
  124.     // if there is extra fields, throws exception  
  125.     public void reorder(Field[] fieldsInNewOrder)  
  126.     {  
  127.         // check fields to see whether they are the same.  
  128.   
  129.         List newFields = new ArrayList();  
  130.         List newColumns = new ArrayList();  
  131.         Map newFieldToColumns = new HashMap();  
  132.         for (int i=0; i
  133.         {  
  134.             newFields.add(fieldsInNewOrder[i]);  
  135.             int index = ((Integer)fieldToColumns.get(fieldsInNewOrder[i])).intValue();  
  136.             newColumns.add(columns.get(index));  
  137.             newFieldToColumns.put(fieldsInNewOrder, new Integer(i));  
  138.         }  
  139.   
  140.         this.fields = newFields;  
  141.         this.columns = newColumns;  
  142.         this.fieldToColumns = newFieldToColumns;  
  143.     }  
  144.   
  145.     //-----------------------------------------------------------------------  
  146.     // row operations  
  147.     //-----------------------------------------------------------------------  
  148.     public int getNumberOfRows()  
  149.     {  
  150.         if (columns.size() == 0return 0;  
  151.   
  152.         List firstColumn = (List)columns.get(0);  
  153.         return firstColumn.size();    // should optimize this - trade speed with space.  
  154.     }  
  155.   
  156.     public List getRow(int row)  
  157.     {  
  158.         List ret = new ArrayList();  
  159.         for (int i=0; i
  160.         {  
  161.             List columnList = (List) columns.get(i);  
  162.             ret.add(columnList.get(row));  
  163.         }  
  164.   
  165.         return ret;  
  166.     }  
  167.   
  168.     public void addRow(List row)  
  169.     {  
  170.         checkSizeMatch(row);  
  171.   
  172.         for (int i=0; i
  173.         {  
  174.             List columnList = getExistingOrNewColumn(i);  
  175.             columnList.add(row.get(i));  
  176.         }  
  177.     }  
  178.   
  179.     public void insertRow(int position, List row)  
  180.     {  
  181.         checkSizeMatch(row);  
  182.   
  183.         for (int i=0; i
  184.         {  
  185.             List columnList = getExistingOrNewColumn(i);  
  186.             columnList.add(position, row.get(i));  
  187.         }  
  188.     }  
  189.   
  190.     private void checkSizeMatch(List row)  
  191.     {  
  192.         if (row == null)  
  193.         {  
  194.             throw new IllegalArgumentException("row is null");  
  195.         }  
  196.   
  197.         if (fields.size() == 0)  
  198.         {  
  199.             throw new IllegalStateException("set the fields before adding rows");  
  200.         }  
  201.   
  202.         // not empty and not equals  
  203.         if (columns.size() != 0 && row.size() != columns.size())  
  204.         {  
  205.             throw new IllegalArgumentException("mismatched column size: " +  
  206.                     "datamatrix num of columns=" + columns.size() + " to be "  
  207.                     + "added row size=" + row.size());  
  208.         }  
  209.     }  
  210.   
  211.     private List getExistingOrNewColumn(int i)  
  212.     {  
  213.         List columnList;  
  214.         if (i >= columns.size()) // empty columns  
  215.         {  
  216.             columnList = new ArrayList();  
  217.             columns.add(columnList);  
  218.         }  
  219.         else // existing  
  220.         {  
  221.             columnList = (List) columns.get(i);  
  222.         }  
  223.         return columnList;  
  224.     }  
  225.   
  226.     // normal, in a data matrix, we need to loop from n-1 to 0  
  227.     // in order to use this in a loop.  
  228.     public void deleteRow(int rowNumber)  
  229.     {  
  230.         for (int i=0; i
  231.         {  
  232.             List columnList = (List) columns.get(i);  
  233.             columnList.remove(rowNumber);  
  234.         }  
  235.     }  
  236.   
  237.     //-----------------------------------------------------------------------  
  238.     // element operations  
  239.     //-----------------------------------------------------------------------  
  240.     // before you call these two methods, make sure you do populate the data.  
  241.     // Otherwise you will get an IndexOutOfBoundsException.  
  242.     public void setCellValueAt(int row, int col, Object value)  
  243.     {  
  244.         if (col >= columns.size())  
  245.         {  
  246.             throw new IllegalArgumentException("try to set value at an uninitialized cell");  
  247.         }  
  248.   
  249.         ((List)columns.get(col)).set(row, value);  
  250.     }  
  251.   
  252.     public Object getCellValue(int row, int col)  
  253.     {  
  254.         if (col >= columns.size())  
  255.         {  
  256.             throw new IllegalArgumentException("try to get value at an uninitialized cell");  
  257.         }  
  258.   
  259.         List column = (List)columns.get(col);  
  260.         if (column == null || row >= column.size())  
  261.         {  
  262.             throw new IllegalArgumentException("try to get value at an uninitialized cell");  
  263.         }  
  264.         return ((List)columns.get(col)).get(row);  
  265.     }  
  266.   
  267.     //-----------------------------------------------------------------------  
  268.     // matrix operations  
  269.     //-----------------------------------------------------------------------  
  270.     // append rows at the bottom  
  271.     public void concatenateRows(DataMatrix dataMatrix)  
  272.     {  
  273.         // check same number of columns first.  
  274.         if (columns.size() != dataMatrix.getNumberOfColumns())  
  275.         {  
  276.             throw new IllegalArgumentException("Two matrices have different "  
  277.                + "number of columns: " + columns.size() + " != " +  
  278.                dataMatrix.getNumberOfColumns());  
  279.         }  
  280.   
  281.         for (int j=0; j
  282.         {  
  283.             ((List)columns.get(j)).addAll(dataMatrix.getColumn(j));  
  284.         }  
  285.     }  
  286.   
  287.     // append columns at the right end  
  288.     public void concatenateColumns(DataMatrix dataMatrix)  
  289.     {  
  290.         // check both matrices have the same number of rows.  
  291.         if (this.getNumberOfRows() != dataMatrix.getNumberOfRows())  
  292.         {  
  293.             throw new IllegalArgumentException("Two matrices have different "  
  294.                + "number of rows: " + getNumberOfRows() + " != " +  
  295.                dataMatrix.getNumberOfRows());  
  296.         }  
  297.   
  298.         int startCol = columns.size();  
  299.         for (int j=0; j
  300.         {  
  301.             fieldToColumns.put(dataMatrix.getField(j), new Integer(j + startCol));  
  302.             fields.add(dataMatrix.getField(j));  
  303.             columns.add(dataMatrix.getColumn(j));  
  304.         }  
  305.     }  
  306.   
  307.     // split the datamatrix to two which are the two sides of this field.  
  308.     // The specified field could be on the right/left/not included.  
  309.     public DataMatrix[] splitFrom(Field field)  
  310.     {  
  311.         int colNum = getColumnNumber(field);  
  312.   
  313.         DataMatrix leftMatrix = new DataMatrix();  
  314.         DataMatrix rightMatrix = new DataMatrix();  
  315.   
  316.         for (int i=0; i
  317.         {  
  318.             leftMatrix.addColumn((Field)fields.get(i), (List)columns.get(i));  
  319.         }  
  320.   
  321.         for (int i=colNum; i
  322.         {  
  323.             rightMatrix.addColumn((Field)fields.get(i), (List)columns.get(i));  
  324.         }  
  325.         return new DataMatrix[] {leftMatrix, rightMatrix};  
  326.     }  
  327.   
  328.     public String toString()  
  329.     {  
  330.         String ret = "";  
  331.   
  332.         if (fields == nullreturn ret;  
  333.   
  334.         // output fields first  
  335.         for (int i=0; i
  336.         {  
  337.             ret += fields.get(i).toString() + " | ";  
  338.         }  
  339.         ret += "\n";  
  340.   
  341.         if (columns == nullreturn ret;  
  342.   
  343.         // output rows  
  344.         for (int i=0; i
  345.         {  
  346.             for (int j=0; j
  347.             {  
  348.                 ret += ((List)columns.get(j)).get(i).toString() + " | ";  
  349.             }  
  350.             ret += "\n";  
  351.         }  
  352.   
  353.         return ret;  
  354.  }  

I am not surprised to see that the only dependency on the headers is just an empty interface, as a flag.

java 代码
 
  1. public interface Field  
  2. {  
  3. }  

If you compare the Swing's TableModel implementations to this one, it's almost the same, except the event handling portion, plus we have several extra methods. This is a good sign that we abstract the responsibility at the right level, not too many responsibilities.

The only relevant missing piece is the serialization. We can either implements Serializable or Externalizable.

In this implementation, I delegate the dynamic memory allocation to Collection classes. I could use a Map that maps fields to columns, but I instead use two Lists for speeding.

I also supplied a toString() method for debugging purpose, but left out other formatters, like toXml(), toCvs() etc.

Since this is more of a design issue, I left out most of the code untested. For simple testing, I created a simple field class:

java 代码
 
  1. public class StringField implements Field  
  2. {  
  3.     private String name;  
  4.   
  5.     public StringField(String name)  
  6.     {  
  7.         this.name = name;  
  8.     }  
  9.       
  10.     public String getName() { return name; }  
  11.   
  12.     public boolean equals(Object obj)  
  13.     {  
  14.         if (!(obj instanceof StringField)) return false;  
  15.   
  16.         StringField field = (StringField)obj;  
  17.         return this.name.equals(field.name);  
  18.     }  
  19.   
  20.     public int hashCode() { return name.hashCode(); }  
  21.   
  22.     public String toString() { return getName(); }  
  23. }  

A real world Field class would have dependencies on date, currency and other factors.

分享到:
评论

相关推荐

    DataMatrix 二维码生成 和解码 C#程序

    DataMatrix 二维码生成 和解码 C#程序,亲测可用。解码是Freytag DataMatrixDecoder A c# implementation to find DataMatrix 'barcodes' in bitmaps and decode them back into a string.

    zxing datamartrix生成点阵二维码

    在本文中,我们将重点讨论如何使用ZXing库在Android平台上生成DataMatrix类型的点阵二维码,并解决可能出现的问题,如生成长条点阵图或图片过小。 ZXing(Zebra Crossing)是一个开源的、跨平台的条码读取和生成库...

    nrfPCA的MATLAB实现

    1)x: your data matrix samples in rows and variables in columns. 2)LV: How many variables to use from data, if not specified all variables are used. 3)method: Optional, will be selected automatically. ...

    PCA implementation

    cov_matrix = cov(data); ``` 3. **特征值分解**:协方差矩阵是一个实对称矩阵,因此可以进行特征值分解。这一步骤将帮助我们找出主成分的方向。MATLAB中的`eig`函数可以完成这个任务。 ```matlab [~, ...

    Open Data Structures (in Java) Pat Morin

    Offered as an introduction to the field of data structures and algorithms, Open Data Structures covers the implementation and analysis of data structures for sequences (lists), queues, priority queues...

    Low-Rank Modeling of Local k-Space Neighborhoods (LORAKS) for Constrained MRI

    with simulated and experimental data for a range of denoising and sparse-sampling applications. LORAKS is also compared against state-of-the-art methods like homodyne reconstruction, `1-norm ...

    i-vector的工具箱

    In the conventional GMM-UBM framework the universal background model (UBM) is a Gaussian mixture model (GMM) that is trained on a pool of data (known as the background or development data) from a ...

    数据库系统原理英文课件:ch16 Concurrency Control.ppt

    The **lock-compatibility matrix** plays a key role here, determining whether a transaction can be granted a lock based on the locks already held by other transactions on the same item. Multiple ...

    android上使用ZXing识别条形码和二维码

    1. **添加依赖**:在项目的`build.gradle`文件中,添加ZXing的依赖库。如果是使用Gradle,可以添加如下依赖: ```groovy implementation 'com.google.zxing:core:3.4.1' implementation '...

    svm matlab版本

    A matlab function libsvmwrite writes Matlab matrix to a file in LIBSVM format: libsvmwrite('data.txt', label_vector, instance_matrix] The instance_matrix must be a sparse matrix. (type must be ...

    Introduction to MATLAB for Engineers

    - **2.7–1 A Student Database**: This example illustrates the creation and management of a student database, showcasing data management techniques and skills necessary for developing software ...

    遥感图像处理论文合集,包含高光谱遥感等多个方向

    “A Large-Scale Benchmark Data Set for Evaluating Pansharpening Performance Overview and implementation”两篇论文提供了评估高光谱和多光谱图像融合(即 pansharpening)性能的大规模基准数据集,这对于比较...

    实验三:数组及其应用.doc

    1. The first program calculates the sum of an array of integers using a for loop. 2. The second program prints the elements of a 2D array in a specific order. 3. The third program finds the maximum ...

    矩阵类 及其基本运算

    例如,可以定义一个名为`Matrix`的类,包含私有字段`rows`、`cols`和`data`,分别表示行数、列数和二维数组数据: ```csharp public class Matrix { private int rows; private int cols; private double[,] ...

    C++深度遍历

    - Uses a 2D array `a` to represent the adjacency matrix of the graph. - Initializes the `AlGraph` structure `G`. - **Edge Construction:** - For each vertex `i`, creates an adjacency list by adding ...

    matpower的最新版本出来了401-matpower4.0b1.zip

     - Use of 'areas' data matrix is now optional.  - Many new tests in test suite. * Bugs fixed:  - Branch power flow limits could be violated when using the option  OPF_FLOW_LIM = 1. * ...

    人口模型的处理,二胎影响

    the data obtained with the existing data, it is concluded that the opening of the second child has a positive effect on China’s economic development, the alleviation of aging, and the increase in the...

    Professional C# 3rd Edition

    Populating a DataSet Class with a Data Adapter 722 Populating a DataSet from XML 723 xx Contents Persisting DataSet Changes 723 Updating with Data Adapters 724 Writing XML Output 726 Working with ADO...

Global site tag (gtag.js) - Google Analytics