Let me revise that. -- Any size data collection that requires FIFO behavior can use a circular buffer. Anything requiring LIFO behavior can use an ArrayList, Vector, etc. Specific types of computations requiring ordered data (and can't be rewritten without that requirement) will typically require specific data structures that aren't helpful to implement using an ArrayList instead of a normal array. Things that are not ordered for the sake of computations can be left unordered until they need to be presented to the user. -- I forgot stacks.

And I mean ordered as in the context of bags vs. ArrayLists. I meant to say "ordered as in maintaining relative orders even after arbitrary insertions and removes like in the List interface." I'll concede that other data structures like heaps, trees, spatial data structures, and even things like hash tables have an order, but it would not help to maintain a List to implement these things. Give me a few examples if you don't mind. Every example that might have more than a hundred elements that I can imagine can be re-engineered without that requirement. My experience and imagination tells me that Lists are only useful/convenient when dealing with user interfaces.

While we're talking about what Java collections could have done... STL containers in C++ have an erase method that takes an iterator as a parameter.

The bugs I used to be plagued by happened several years ago and the precise nature of the problems I had are now lost in the mists of time. It was largely to do with things mysteriously not getting ticked when I was expecting them to be, or things getting ticked before the thing that had spawned them was ticked causing some odd behaviours related to my assumption that they'd always be ticked in a particular order. However I do remember making a note to myself about it. Always use Lists, never Bags, when dealing with game entities. Mostly everything else was ok with bags - particles, sprites, etc. - especially particles and sprites actually as these tend to come and go very very frequently compared to anything else.

Sounds like a bug in your Iterator or for loop, especially if objects are getting skipped entirely. If I add objects in the middle of a game loop, then I either make sure it doesn't break anything or wait until the next update cycle to acknowledge new objects. Also, unless you need a specific order instead of just consistent ordering, you can still maintain relative order inside a multiple-stage update cycle. Specific ordering might include things like updating platform game entities in order from left to right, executing update functions on objects in a FIFO manner, or pulling game entities from a priority queue to determine update order. All of those things are easiest to accomplish using a different data structure or by sorting the elements of the bag at the start of an update cycle. If consistent ordering is good enough, just wait to the last time you loop through each element to take them out. The order won't change if you don't remove anything. Plus, if you start from the beginning assuming the opposite, then you won't have a problem if you change to a non-list-based data structure.

To be a weenie...I can't think of a hashing situation that would have any meaningful ordering...assuming the hash generation isn't awful.

I can think of a few. The most useful is where you can use the hashing order to generate a more optimal iteration order.

I used Morton order to generate hashes for minecraft chunks in Tectonicus. The actual iteration order is unimportant (from a correctness point of view), but using morton order gives a much higher cache hit rate for the various geometry caches as it churns through the collection.

@DrewLols If you're doing it in a for loop just remove the item at the current index and decrement i by 1.

Well, yeah, but that would force the removal to happen within the loop rather than an object in a loop. I've allowed objects contained in a level to be responsible for their own removal in a few cases. I guess I could flag the item to be removed and then remove it in the loop. Least then the removal would be safe.

Quote

It's pretty easy to make it generic. I did so and implemented Collection and Iterable as well (the latter for use in for each loops)

Yeah, I made mine generic as well. Apparently, though, for each loops are blasphemous (thank you spell checker).

Uhhhhh... Don't suppose there's a safe way to remove an element from a Bag while iterating through it, is there? I want to use Bag, but it seems that there is no iterator method.

There is. Use the exact same removal process and adjust the index value back to the index of the gap the removal made before it got filled in with a new value. You can implement an Iterator based loop and remove them the same way as you would a LinkedList or HashSet. Here's the example I used in my Shared Code topic

It's actually not weird at all. A bag is also called a "multiset". It's like a set because it stores objects with its own internal organization and has no guarantee on the order of elements returned by an iterator. It differs from a set because it lets you store multiple copies of each object. Adding an object increases the count and removing one decreases it. You could implement something like this so that internally a bag stores multiple copies of each object and keeps one reference for each one; Or, you could implement a bag such that it stored one reference to each instance of an object and an integer counter to keep track of each additional insertion of the same object.

Like I explained in my "tutorial" on the subject, bags/multisets can also be implemented similarly to a map. You could almost do the same thing using a Map, but it would require a lot of extra work on the user's end. (Get a value, add one, put the new value back in for adding objects. And getting a value, subtracting one, removing a key-value pair if zero, putting the new value back in otherwise. It requires a lot of extra work and is a lot easier to implement as a separate class.) If you take a look at the Apache Commons implementations, they include a HashBag and a TreeBag, which are analogous to HashSets and TreeSets and HashMaps and TreeMaps. That's why I tried to be precise and say "Array Backed Bag." Kappa's post did not reference that fact.

The Apache implementation uses the .equals method of your class to detect duplicates, like Java collections do. It considers objects identical if equals returns true. It doesn't define buckets/ranges, but you could accomplish something like that if you wrote equals a certain way. Mine and Kappa's implementations do not use .equals. (All three sources implement a bag that either violates the Collections interface or could not implement it without doing so.) The array based version is usually more useful in game programming, especially if you only keep one copy or don't override equals.

You might use array based bags in your game loop or as generic containers as an alternative to ArrayLists or HashSets. You might use a HashBag to keep count of stackable items in a player's inventory, calling add(obj) or add(obj, count) for every item picked up and remove(obj) for every item used.

The array based version will have faster adds and removes. It can provide O(n) based contains(obj) and count(obj) function while other implementations could provide O(1) or O(log n) time. The array based implementations will take less space if you keep one copy or only a few copies, but the others will take less space if you have many duplicates.

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org