11

Is it possible to store a mixture of object types in an ArrayList? If so how?

This is what I have tried so far:

List<Object> list = new ArrayList<Object>();

list.add(new String("Hello World"));
list.add(new Integer(1));
list.add(new Long(1l));

for (i = 0; i < list.size(); i++) {
    if (list.get(i) instanceof String){
        sqlPreparedStatement.setString((i+1), (String) list.get(i));
    } else if (list.get(i) instanceof Integer) {
        sqlPreparedStatement.setInt((i+1), (Integer) list.get(i));
    } else if (list.get(i) instanceof Long) {
        sqlPreparedStatement.setLong((i+1), (Long) list.get(i));
    }
}

But it throws a casting exception.

Thanks in advance for any input!

travega
  • 8,284
  • 16
  • 63
  • 91
  • 7
    Define "doesn't work". – Oliver Charlesworth Jun 10 '11 at 00:12
  • Why parametrize with Object? What do you get with that? – Jeremy Jun 10 '11 at 00:13
  • 8
    @Jeremy: It avoids irritating wiggly yellow underlining in Eclipse! – Oliver Charlesworth Jun 10 '11 at 00:15
  • 1
    Show the stack trace of your exception, please. The interesting point would be the line number of the ClassCastException (and mark this number in the source). The source looks fine, apart from some optimizations like in the answer from *scientiaesthete*, and the superfluous `new String(...)` there. – Paŭlo Ebermann Jun 10 '11 at 00:56
  • @Paulo is correct. The right way to figure out what is going wrong is to look at a stack trace. Does the order of your List match what's happening in your PreparedStatement? – Atreys Jun 10 '11 at 11:41

3 Answers3

20

This is what you should have:

List<Object> list = new ArrayList<Object>();

list.add(new String("Hello World"));
list.add(new Integer(1));
list.add(new Long(1l));

for (Object obj: list) {
    if (obj instanceof String){
        sqlPreparedStatement.setString((String) obj);
    } else if (obj instanceof Integer) {
        sqlPreparedStatement.setInt((Integer) obj);
    } else if (obj instanceof Long) {
        sqlPreparedStatement.setLong((Long) obj);
    }
}
scientiaesthete
  • 919
  • 9
  • 20
  • 6
    There's no need for the "new String()" by the way. – Paul Cager Jun 10 '11 at 00:43
  • 2
    @scientiaesthete : Apart from List#get(int) getting called twice as many times as in yours, and the use of an iterator, there is no difference between your code snippet and the OP's. Am I missing something here - what is the difference with regard to casting? – bguiz Jun 10 '11 at 00:43
  • 1
    @bguiz The setXXX() signatures are different. The OP's had `setXXX(int, XXXthing)`, this one has just `setXXX(XXXthing)`. One of them doesn't match the API, methinks. – Atreys Jun 10 '11 at 01:02
  • 1
    @Atreys: yep, this "answer" is in the wrong, and in fact the OP's code is the better of the two since it in fact compiles and gives the for loop an int parameter that can be used in the prepared statement's method calls. I have to wonder how this answer got 5 up-votes? Down-voted by me. – Hovercraft Full Of Eels Jun 10 '11 at 01:20
  • 1
    Actually there's no need for any of the `new`s with autoboxing. – trutheality Jun 10 '11 at 02:21
  • @Paul Cager That was actually just to demonstrate the different object types. As this is just demo code I wanted to clearly demonstrate the different objects types being used. – travega Jun 10 '11 at 03:23
  • @Atreys @scientiaesthete @Hovercraft : I still can't see how this would make a difference w.r.t. casting... – bguiz Jun 10 '11 at 07:00
  • @bguiz. You're right. it doesn't make a difference w.r.t. casting in the visible code – Atreys Jun 10 '11 at 11:35
13

Sorry to crash your parade, but you shouldn't be using an ArrayList of 3 (or any) different types to begin with. If the information is related, create a class that holds the related information and create an ArrayList that holds only one type: objects of this class.

Edit 1:
For instance say a class to hold the data like so:

class SqlData {
   private String textData;
   private int intData;
   private long longData;

   public SqlData(String textData, int intData, long longData) {
      this.textData = textData;
      this.intData = intData;
      this.longData = longData;
   }

   public String getTextData() {
      return textData;
   }

   public int getIntData() {
      return intData;
   }

   public long getLongData() {
      return longData;
   }

}

and used like so:

  List<SqlData> sqlDataList = new ArrayList<SqlData>();
  sqlDataList.add(new SqlData("Hello World", 1, 11L));

  for (int i = 0; i < sqlDataList.size(); i++) {
     try {
        sqlPreparedStatement.setString(i + 1, sqlDataList.get(i).getTextData());
        sqlPreparedStatement.setInt(i + 1, sqlDataList.get(i).getIntData());
        sqlPreparedStatement.setLong(i + 1, sqlDataList.get(i).getLongData());
     } catch (SQLException e) {
        e.printStackTrace();
     }
  }
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 2
    This approach is more descriptive. But I think we don't need a `sqlDataList` and a `for` loop here. – Genzer Jun 10 '11 at 03:49
  • +1 @Hovercraft : Just to counteract the "revenge" downvote you likely got; AND I also agree that you shouldn't be mixing objects of different types within any List in the first place; WITH the possible exception of types which are inherited (obeys the "is-a" rule) – bguiz Jun 10 '11 at 06:56
  • 1
    Even though you shouldn't mix objects of different types in a List, that doesn't mean people can't and that there isn't software out there that has this type of code in it. – Atreys Jun 10 '11 at 11:39
  • 1
    @Atreys: It's not a matter of "can't", but more of a matter of creating software that is difficult to debug, maintain and extend when one follows bad practices such as this. – Hovercraft Full Of Eels Jun 10 '11 at 12:40
  • I really like this answer because it teaches you the right way to do things! So often, people give answers just to give answers, rather than looking at the big picture! – Sam Malayek Nov 07 '17 at 05:38
5

Why are you making this so hard? PreparedStatement has a setObject() method - just use that:

    List<Object> list = new ArrayList<Object>();
    list.add(new String("Hello World"));
    list.add(new Integer(1));
    list.add(new Long(1l));
    for (int i = 0; i < list.size(); i++)
        sqlPreparedStatement.setObject(i + 1, list.get(i)); // NOTE: columns count from 1

NOTE: The java SQL API counts everything from 1, not from zero, so columns are numbered 1...size() and not 0...size()-1

Bohemian
  • 412,405
  • 93
  • 575
  • 722