Use lists when you're adding or removing elements ad-hoc, and the order matters.
If the order doesn't matter, and you know you're never going to store more than "X" items, another thing you can do is use one array of length X, and whenever an item is removed, you over-write that item with the last item of the array, and decrement a "used" counter. That can be a lot more efficient.
Lists are for storing small things, where it's not worth your time to think up something more specific. But they're not very efficient, so they don't scale very well if the data size grows very large, or if you're often searching for specific list elements.
One important performance aspect is that subsequent list items may or may not be adjacent in memory. So if you make a list all at once at the start of your program, it will be fairly efficient. But if there was a separate thread also making a list at the same time, now you have two interleaved lists in memory, which means double the time to access list elements sequentially (it will have to cache twice as much memory to process the same elements from one list).
Also, as elements are added and removed during the program run, it's very unlikely that they'll stay in "correct" order in memory, so performance of the list will gradually decay. Feel free to ignore this if you're making lists < 100 elements, or the entire list is only processed occasionally. But if your entire list of things is being processed every frame, or you have e.g. 100 stars in a gravity sim and they're being processed against the other 99 stars, then nah, lists will be horrible, especially if new simulation elements could be added or destroyed after the start.
One good practice in object oriented languages is wrapper classes for libraries and containers. This will give you the most bang for your buck of most encapsulation choices.
~~~
A core OO principle is single responsibility: one "thing" is responsible for handling one "decision", and it's the only thing that's dependent on that design decision. e.g. say you decided to use Boost's dynamic arrays in your program. Just about the worst possible thing you could do would be to add "boost.h" to every cpp file and scatter Boost code all throughout your program. Now, your boss says "we can't use Boost library for <reason X>, remove all Boost code from the program". Now, to reverse that one decision, you need to change 1000's of lines of code and test various systems, and it's likely things are going to break in unpredictable ways, since you need to do 1000's of changes all at once, and can't incrementally change: it's all-or nothing.
So, a more clever way is to get the boost library, link that into a single cpp file, then make a .h file for that cpp file that makes an interface that only uses selected Boost features in a controlled fashion. After doing that, you'll find that it's quite easy to make your own replacement for the boost features you used, then test that in isolation until it's perfect. you can then roll that out as a replacement for the original feature, and it's more common than not that it's just going to work first try, or if not, the errors tend to be more easy to pin down and explain why they happened. You can also put debug statements into the wrapper layer: they can do bounds-checking, input-output checking etc.
So, similarly, when you make data structures that rely on some specific language feature, wrap them up behind a generic class interface. You'll be able to experiment with different choices much more easily when there's only one class which knows whether you decided to use a List or a Vector.