tags:

views:

2647

answers:

6

I'm a newbie java programmer and having some difficulties with [Array]List. Specifically, I'm trying to read a CSV file into a list of lists (of strings), pass it around for getting some data from a database, build a new list of lists of new data, then pass that list of lists so it can be written to a new CSV file. I've looked all over and can't seem to find anything like that. I'd rather not use simple arrays since the files will vary in size and I won't know what to use for the dimensions of the arrays. I have no issues dealing with the files. I'm just not sure how to deal with the list of lists.

Most of the examples I've found will create multi-dimensional arrays or perform actions inside the loop that's reading the data from the file. I know I can do that, but I want to write object-oriented code. If you could provide some example code or point me to a reference, that would be great.

Thanks, Rob

+2  A: 
ArrayList<ArrayList<String>> listOlists = new ArrayList<ArrayList<String>>();
ArrayList<String> singleList = new ArrayList<String>();
singleList.add("hello");
singleList.add("world");
listOlists.add(singleList);

ArrayList<String> anotherList = new ArrayList<String>();
anotherList.add("this is another list");
listOlists.add(anotherList);
tster
+2  A: 

Here's an example that reads a list of CSV strings into a list of lists and then loops through that list of lists and prints the CSV strings back out to the console.

import java.util.ArrayList;
import java.util.List;

public class ListExample
{
    public static void main(final String[] args)
    {
     //sample CSV strings...pretend they came from a file
     String[] csvStrings = new String[] {
       "abc,def,ghi,jkl,mno",
       "pqr,stu,vwx,yz",
       "123,345,678,90"
     };

     List<List<String>> csvList = new ArrayList<List<String>>();

     //pretend you're looping through lines in a file here
     for(String line : csvStrings)
     {
      String[] linePieces = line.split(",");
      List<String> csvPieces = new ArrayList<String>(linePieces.length);
      for(String piece : linePieces)
      {
       csvPieces.add(piece);
      }
      csvList.add(csvPieces);
     }

     //write the CSV back out to the console
     for(List<String> csv : csvList)
     {
      //dumb logic to place the commas correctly
      if(!csv.isEmpty())
      {
       System.out.print(csv.get(0));
       for(int i=1; i < csv.size(); i++)
       {
        System.out.print("," + csv.get(i));
       }
      }
      System.out.print("\n");
     }
    }
}

Pretty straightforward I think. Just a couple points to notice:

  1. I recommend using "List" instead of "ArrayList" on the left side when creating list objects. It's better to pass around the interface "List" because then if later you need to change to using something like Vector (e.g. you now need synchronized lists), you only need to change the line with the "new" statement. No matter what implementation of list you use, e.g. Vector or ArrayList, you still always just pass around List<String>.

  2. In the ArrayList constructor, you can leave the list empty and it will default to a certain size and then grow dynamically as needed. But if you know how big your list might be, you can sometimes save some performance. For instance, if you knew there were always going to be 500 lines in your file, then you could do:

List<List<String>> csvList = new ArrayList<List<String>>(500);

That way you would never waste processing time waiting for your list to grow dynamically grow. This is why I pass "linePieces.length" to the constructor. Not usually a big deal, but helpful sometimes.

Hope that helps!

Brent Nash
Your example, while rather detailed, is also rather misleading. Reading CSV file line by line and splitting lines at comma with no regard for surrounding quotes is asking for trouble.
ChssPly76
Brent Nash
+1  A: 

The example provided by @tster shows how to create a list of list. I will provide an example for iterating over such a list.

Iterator<List<String>> iter = listOlist.iterator();
while(iter.hasNext()){
    Iterator<String> siter = iter.next().iterator();
    while(siter.hasNext()){
         String s = siter.next();
         System.out.println(s);
     }
}
Vincent Ramdhanie
+4  A: 

If you are really like to know that handle CSV files perfectly in Java, it's not good to try to implement CSV reader/writer by yourself. Check below out.

http://opencsv.sourceforge.net/

When your CSV document includes double-quotes or newlines, you will face difficulties.

To learn object-oriented approach at first, seeing other implementation (by Java) will help you. And I think it's not good way to manage one row in a List. CSV doesn't allow you to have difference column size.

xrath
+1 for suggesting OpenCSV. However, it **is** good to *try* to implement CSV reader / writer yourself, especially for a java newbie - you learn lots of things along the way.
ChssPly76
+1  A: 

I'd second what xrath said - you're better off using an existing library to handle reading / writing CSV.

If you do plan on rolling your own framework, I'd also suggest not using List<List<String>> as your implementation - you'd probably be better off implementing CSVDocument and CSVRow classes (that may internally uses a List<CSVRow> or List<String> respectively), though for users, only expose an immutable List or an array.

Simply using List<List<String>> leaves too many unchecked edge cases and relying on implementation details - like, are headers stored separately from the data? or are they in the first row of the List<List<String>>? What if I want to access data by column header from the row rather than by index?

what happens when you call things like :

// reads CSV data, 5 rows, 5 columns 
List<List<String>> csvData = readCSVData(); 
csvData.get(1).add("extraDataAfterColumn"); 
// now row 1 has a value in (nonexistant) column 6
csvData.get(2).remove(3); 
// values in columns 4 and 5 moved to columns 3 and 4, 
// attempting to access column 5 now throws an IndexOutOfBoundsException.

You could attempt to validate all this when writing out the CSV file, and this may work in some cases... but in others, you'll be alerting the user of an exception far away from where the erroneous change was made, resulting in difficult debugging.

Nate
A: 

Something like this would work for reading:

String filename = "something.csv";
BufferedReader input = null;
List<List<String>> csvData = new ArrayList<List<String>>();
try 
{
    input =  new BufferedReader(new FileReader(filename));
    String line = null;
    while (( line = input.readLine()) != null)
    {
     String[] data = line.split(",");
     csvData.add(Arrays.toList(data));
    }
}
catch (Exception ex)
{
      ex.printStackTrace();
}
finally 
{
    if(input != null)
    {
     input.close();
    }
}
Droo