adickinson
adickinson

Reputation: 553

Is there a collection more suitable for this scenario than an ArrayList?

I have a method that will need to return a set of data, and the current plan is to use an ArrayList of ArrayList, however I am unsure if there is a collection that is more suitable.

This abstract nature lead me to believe that each row would be best represented by an ArrayList, as this could has entries added to it before finally adding that ArrayList to the main ArrayList.

Is there a better way to do this, particularly with regards to performance?

Upvotes: 1

Views: 64

Answers (3)

Jimenemex
Jimenemex

Reputation: 3166

In Java, there is the concept of Jagged Arrays. It's essentially that same as you describe. You can either implement it in one of two ways.

  1. A 2-D Array where you initialize the rows when you declare the variable, and declare each column when you need to use it.
  2. An array of Lists, where you declare the rows when you initialize it, and just add items (columns) to each index in the rows when needed.

2-D Array Approach

// Initialize the rows only for now
String[][] arr = new String[3][];

// Process some data to know the column sizes of each
// ...
arr[0] = new String[] {"Foo", "Bar", "FooBar"};
arr[1] = new String[] {"Jane", "Doe"};
arr[2] = new String[] {"Peter", "Parker", "Tony", "Stark"};
// ...
// Printing 
for(String[] row : arr) {
    System.out.println(Arrays.ToString(row));
}

Array of Lists Approach

// Initialize the rows only for now
List<String> [] arr = new List[3];

// Process some data
// ...
List<String> list1 = new ArrayList<>();
list1.add("Foo");
list1.add("Bar");
list1.add("FooBar");
List<String> list2 = new ArrayList<>();
list2.add("Jane");
list2.add("Doe");
List<String> list3 = new ArrayList<>();
list3.add("Peter");
list3.add("Parker");
list3.add("Tony");
list3.add("Stark");
// ...
// Printing
for(int i = 0 ; i < arr.length; i++) {
    List<String> temp = arr[i];
    System.out.println(temp);
}

Based on your needs, since you know that your data set will always remain fixed. You just don't know the length of each row in the Jagged Array. Based on this, Go with the plain 2-D approach.

Now, If you think the requirements are going to change later down the road, or you like the useful methods that ArrayLists have (contains etc.) then I would go with an Array of ArrayLists.

You will not notice a significant performance difference between the two implementations unless the data set is huge. If you are experiencing performance issues then go with the 2-D array approach.

If it was up to me, I would go with the Array of ArrayLists approach. I would go with this because:

  1. ArrayLists can be expanded easily if your data set would ever happen to change.
  2. ArrayLists also offer plenty useful methods that are easier to just call then have to implement yourself.
  3. ArrayLists also they implement Iterable, Collection, and List, and can therefore be used in a lot of API calls that are interface-based.

Some methods that ArrayLists can do are here.

More about Jagged arrays can be found here.

Upvotes: 1

Juraj
Juraj

Reputation: 778

There is little complicated way (But performance is good because of direct access to data) how the have the collection which has the row represented by groupKey (rowKey) and colums represented by comlunKey. Here you can add optional object. In simple way it is map of maps.

/**
 * representation of bi dimensional array with dynamical size
 *
 * @param <K> - type of row key
 * @param <L> - type of columne key
 * @param <V> - type of value
 */
public class CustomCollection<K, L, V> extends HashMap<K, Map<L, V>> {

public CustomCollection() {

}

/**
 * add new entry on particular row and column
 *
 * @param rowKey    - key of row
 * @param columnKey - key of column
 * @param value     - value to add
 */
public void addNewEntry(K rowKey, L columnKey, V value) {
    Map<L, V> columns = get(rowKey);
    if (columns == null) {
        columns = new HashMap<>();
        put(rowKey, columns);
    }
    columns.put(columnKey, value);
}

/**
 * get exact value on current row represented by {@code rowKey} and column represented by {@code columnKey}
 *
 * @param rowKey    - key of row
 * @param columnKey - key of column
 * @return
 */
public V getExactValue(K rowKey, V columnKey) {
    Map<L, V> columns = get(rowKey);
    if (columns == null) {
        return null;
    }
    return columns.get(columnKey);
}

/**
 * get colums values as map
 *
 * @param rowKey - key of row
 * @return
 */
public Map<L, V> getColumsAsMap(K rowKey) {
    return get(rowKey);
}

/**
 * get colums values as map
 *
 * @param rowKey - key of row
 * @return
 */
public Collection<V> getColumsAsCollection(K rowKey) {
    Map<L, V> columns = get(rowKey);
    if (columns == null) {
        return null;
    }
    return columns.values();
}

/**
 * remove value on particular index
 *
 * @param rowKey
 * @param columnKey
 *
 * @return - removed item
 */
public V removeExactValue(K rowKey, L columnKey){
    Map<L, V> columns = get(rowKey);
    if (columns == null) {
        return null;
    }
    return columns.remove(columnKey);
}

@Override
public String toString(){
    final StringBuffer stringBuffer = new StringBuffer();
    keySet().forEach(rowKey -> {
        stringBuffer.append(rowKey + " = {");
        Map<L, V> columns = get(rowKey);
        if (columns != null) {
            columns.entrySet().forEach(entry -> {
                stringBuffer.append("["+entry.getKey()+"="+entry.getValue()+"]");
            });
        }
        stringBuffer.append("}\n");
    });
    return stringBuffer.toString();
}
}

// Sample how to use it

public static void main(String [] args){
    CustomCollection<Integer,Integer,String> customCollection = new CustomCollection<>();
    customCollection.addNewEntry(1,1, "v_1_1");
    customCollection.addNewEntry(1,2,"v_1_2");
    customCollection.addNewEntry(2,1,"v_2_1");
    customCollection.addNewEntry(2,2,"v_2_2");
    customCollection.addNewEntry(2,3,"v_2_3");
    System.out.println(customCollection.toString());
    System.out.println("After remove:");
    customCollection.removeExactValue(2,2);
    customCollection.removeExactValue(2,3);
    System.out.println(customCollection.toString());
}

Upvotes: 1

gil.fernandes
gil.fernandes

Reputation: 14611

IF the number of rows is fixed (let us say 10) and the number off columns not, then you can use an array of lists like e.g:

// Use whatever type you need instead of Integer.
@SuppressWarnings("unchecked")
List<Integer>[] arrayOfLists = new ArrayList[10];

This code issues a warning "generic array creation". For this reason I have added @SuppressWarnings("unchecked").

Upvotes: 1

Related Questions