When designing the data structure for such problems, its easier to start by defining the interface for SpreadSheet. Once all the operations (read operations, create operations, modify operations and delete operations) are defined, the characteristics required (sequential access,direct access etc.) of the data structure would become obvious.
In a spreadsheet, one would need mechanisms to directly access columns, rows or cells using indices/names which calls for a Map. One would also need sequential access (iteration) over rows and columns which calls for a List. So a MapList interface is what you need. Now we need to choose the implementation. Since deletion can happen anywhere within the list of rows or columns, a LinkedList implementation seems best. It would also allow constant time operations for list re-arranging. Summing up, a LinkedListMap would be the best choice for rows and columns.
class SpreadSheet:
LinkedListMap<String,Row> rows;
// RowKey --> Row. Row has data. This allows direct access and looping.
LinkedListMap<String,Col> cols;
//only metadata - name,sort status, visible/invisible...
//This allows direct access and looping.
class Row:
LinkedListMap<String,Cell> cells; //colKey --> cell
//This allows direct access and looping.
class Cell:
Object value;
EType dataType; //enum for data type
class Col:
String name;
ESortState sortState; //ASC, DESC, NONE
boolean visible;
To access a particular cell from sheet:
rows.get(rowKey).getValue(cells.get(colKey).getName())
With the above data structures, you should be able to easily implement even complex operations like getting a sub-spreadsheet (a rectangular selection).