Regardless of the approach, you will need code with the responsibility to do it correctly. And you want that code to be in a single place.
Why a single place? On one hand, if it has any bug, you only have to fix it there. On the other, if you want to optimize your code, you only have to do it there. Having the code responsible of this in a single place is what enables you to start with a subpart implementation and optimize it later.
Thus, make a class. The only way to access and modify the list should be through the class, making it imposible to do it wrong or forget doing it somewhere. How you compute the sum, is an implementation detail.
Oh, by the way, if you find yourself needing this code somewhere else, a class is easy to reuse. By properly encapsulating you will need to solve this problem less often. In fact, for problems that come very often, it is faster to find a third party library.
I like the idea of making more performant software by default (with the fall of Moore's law and all)… YAGNI. Probably. Implement the one you find simpler and easier to read. If your performance is poor (profile), then optimize it.
With that said, let us pretend you have decided that this part of the code has bad performance, what can we do?
I give you these guidelines:
- Not doing something is faster than doing it.
- If you need it done, do it later.
- If you need it done right away, do it less often. Can you reuse results?
- If you need it done often, have a third party solution deal with it.
- If you have to do it, then do it faster.
So, what happens less often? Adding and removing elements or querying the sum?
If you query the sum more often than you add or remove items, you don't want to compute the sum each time. The guideline suggest to update the sum when items are added or removed. That way you can have the value already computed and return it.
On the other hand, when you add and remove items more often than you query the sum, the guideline suggests to compute the sum when you query it.
There is a third way: you can do it lazily (doing it later). Have cached sum value. Store a bool that tells you if that cached sum is valid or not. If it is, then just return it. If it isn't, then compute it again, mark it valid, and return it. When you add or remove an item, you mark it invalid.
We can actually make it more complicated: keep track of what items has been added or removed since the last computation. There is a word for that: overengineering.
By the way, you could have a not thread-safe solution. Let me rephrase that: you almost certainly have a not thread-safe solution unless you intentionally made it so and know what you are doing.
There are 2 hard problems in computer science: cache invalidation, naming things, and off-by-1 errors.
-- Leon Bambrick
Or, you know, you could not do it. Chances are that whatever solution you come up with is fine, and never shows up as a problem in profiling. We don't only want to optimize the code, but also the development process. Remember the guidelines.
Also remember that premature optimization something something.