2

I was doing a little coding challenge at work and we were required to perform quite a bit of business logic that result in a List of Lists of Integers. I had the required information in an ArrayList of ArrayList of Integers, but could never figure out how to convert it to the correct return type.

Also many variations on using T or Object as they types. I've searched stack overflow and there are many similar problems, but none that give me a straight answer; they're more conceptual.

Problem to answer: The output MUST be a

    List<List<Integer>> (not negotiable). 

Given an existing:

    ArrayList<ArrayList<Integer>> **currentArrayList** HOW do you convert that to a List<List<Integer>>. 

I no longer need the code, but It's going to drive me nuts to be able to do all the logic but not a simple type conversion.

List<List<Integer>> returnList(){
    ArrayList<ArrayList<Integer>> currentArrayList = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> innerList = new ArrayList<Integer>();
    innerList.add(new Integer(123));
    currentArrayList.add(innerList);
    List<List<Integer>> newList = ????
    // ??Logic to copy over currentArrayList into newList???
    return newList;
} 
6
  • List<List<Integer>> currentArrayList = new ArrayList<>(); Commented Jul 24, 2018 at 21:36
  • 1
    What makes you think you should be able to do this conversion? A List<ArrayList<Integer>> isn't a List<List<Integer>>. Commented Jul 24, 2018 at 21:36
  • @VeeArr Doesn't mean it can't be converted. Commented Jul 24, 2018 at 21:37
  • @shmosel That's certainly true; I just wanted to point out that there won't be a way to do so that's necessarily type-safe, similar to how casting an Object to a specific subclass wouldn't be (though not for the same reasons). Commented Jul 24, 2018 at 21:39
  • 1
    You don't have to cast. You can copy them to a new list: List<List<Integer>> newList = new ArrayList<>(currentArrayList); Commented Jul 24, 2018 at 21:41

5 Answers 5

1

You should work against interfaces and remove extact implementation detail. In your example, use a reference to List<List<Integer> instead, since you can add any List implementation to it, including your ArrayList.

public List<List<Integer>> returnList(){
    List<List<Integer>> currentList = new ArrayList<>();
    List<Integer> innerList = new ArrayList<>();
    innerList.add(123);
    currentList.add(innerList);
    return currentList;
}

If you need to return a new List, simply wrap it into another one:

return new ArrayList<>(currentList);

Alternatively, make the returned List unmodifiable:

return Collections.unmodifiableList(currentList);

Edit

If you are really stuck using ArrayList you can cast it to a List with

return (List) currentArrayList;

This cast throws a compiler warning, but you can ignore it with @SuppressWarnings("unchecked") since you know that the correct type is returned.

Sign up to request clarification or add additional context in comments.

1 Comment

Typo: currentArray should be currentList.
1

Just change the declaration type like this since ArrayList extends from AbstractList which implements List:

List<List<Integer>> returnList(){
    List<List<Integer>> currentArrayList = new ArrayList<>();
    List<Integer> innerList = new ArrayList<>();
    innerList.add(new Integer(123));
    currentArrayList.add(innerList);
    return currentArrayList;
}

Running example on GitHub

4 Comments

This is the right way to do it from the beginning, but if legacy code is being worked on, it might not be easily possible to go back and declare it simply as a List of Lists.
@Treebasher I suppose a little coding challenge is not legacy code and the methods return type does not get modified here, only implementation detail.
@lealceldeiro As i stated in my answer, you should probably change ArrayList<Integer> innerList = new ArrayList<>() to List<Integer> innerList = new ArrayList<>(); as well.
@Glains sure! That was my intention from the beginning. I totally missed that one :) Thank you very much for pointing that out!
0

If you are okay with suppressing unchecked cast warnings, its as simple as:

@SuppressWarnings("unchecked")
private static List<List<Integer>> convertToInterfaceVersion(ArrayList<ArrayList<Integer>> original) {
  return (List) original;
}

This is possible because List is just an interface which ArrayList ultimately implements. But this is also a prime example of why people usually don't declare their ArrayLists explicitly as ArrayLists, but rather just as Lists—it completely prevents this situation in the first place.

Comments

0

Rule of thumb: Always try coding to an interface.

If you are not the beginning of the code, that is, there is no chance to change the return type, you can use the following, it uses ctor of ArrayList to copy passed List

List<List<Integer>> returnList() {
   ArrayList<ArrayList<Integer>> currentArrayList = new ArrayList<>();
   ArrayList<Integer> innerList = new ArrayList<>();
   innerList.add(123);
   currentArrayList.add(innerList);
   List<List<Integer>> newList = new ArrayList<>(currentArrayList);
   return newList;
}

Otherwise, if there is chance to change its return type, just alter the declaration type to List<List<Integer>>, it applies the rule of thumb.

List<List<Integer>> returnList() {
   List<List<Integer>> currentArrayList = new ArrayList<>();
   List<Integer> innerList = new ArrayList<>();
   innerList.add(123);
   currentArrayList.add(innerList);
   return currentArrayList;
}

Comments

0

As others mentioned, program to an interface.

Your one-line solution is:

List<List<Integer>> newList = Collections.unmodifiableList(currentArrayList);

But this has an obvious downside:
You can not add new elements to it.

It makes sense if you think about it.
While you could add a LinkedList<Integer> to a List<List<Integer>>, you can not add a LinkedList<Integer> to a List<ArrayList<Integer>>.

If you need the ability to modify the list, then you can create a new List:

List<List<Integer>> newList = new ArrayList<>(currentArrayList);

This means that the outer list is copied.

There is a last solution that you should not take, and is using a raw type:

List<List<Integer>> newList = (List) currentArrayList;

While this will compile, it can break later if someone adds e.g. a LinkedList<Integer> into that list, while you still expect every element in that list to be an ArrayList<Integer>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.