If you denote n
the number of atoms of your array, printing them with a loop is O(n)
, whereas flattening them is in O(n²)
. This is because adding Python lists is linear in the size of both lists.
Besides that, when you flatten the list, you actually store the intermediate results in the RAM, whereas the loop way is lazy-evaluated, meaning you only have O(1)
memory consumption. Writing to memory is a slow operation, which might also contribute to the poor performances.
EDIT: Some details (that are lacking in my original answer).
- When you print something, that something has to be stored somewhere (at least in a buffer). So when I talk about memory complexity, I do not take into account that aspect. To put it another way, the complexity I am talking about is the extra space required.
- With atom, I meant the bottom elements.
[["a", "b"], ["c", "d"]]
has four atoms. Flattening a list of lists using sum
has a complexity that is quadratic with respect to the number of atoms (in the case of a list of singletons, that high bound is reached). However, simply traversing the array has a complexity that is linear with respect to the number of atoms.
- Don't let the join fool you into thinking you're doing something more than if you did not put it. Your last list is equivalent to
print(*(j for i in _2DArray for j in i))
Note that this last line really shows that the difference between the two ways to print the elements of the array is in the non-lazy flattening vs lazy traversal.
A note about merging python lists.
The theoretical problem
When you add two lists in python, you actually move (at least) on of the two lists, which simply means copying all of its elements. This operation requires writing to memory. The main problem of flattening (done this way) is that you keep copying some elements again and again. There is a way to make it linear, which is the following
result = []
for line in _2DArray:
result.extend(line)
however, since it Python you have a very thin control over how memory is managed, you can't be sure (unless you deep into the specs, which you usually want to avoid) that it's how it is done. The other way that it could be done would be
result = []
for line in _2DArray:
temp = result
result = line[:]
result.extend(temp)
This is clearly much worse. Well, when you simply sum
a list of lists, you can't really tell which one is going to happen.
The practical problem
In any case, even if what was actually done was the linear-time solution, you still copy arrays a lot, which implies you write to memory more than if you simply did it with generators.