Hi!
I’m experimenting with containers that cache up to ~100 actors whose lifetimes are not controlled by the owner of the container.
`// simplified example
UPROPERTY()
TSet<TObjectPtr> Tracked;
// elsewhere
Tracked.Add(SomeActor);
…
SomeActor->Destroy(); // or gets GC-ed later`---
1. Hash stability / look-up correctness
* When `SomeActor` is destroyed and collected, `TObjectPtr` starts returning `nullptr`.
* Does the *stored* key’s hash value change?
* Will `Tracked.Contains(SomeActor)` still enter the correct bucket?
* Could two “dead” keys collide (all hashing to `0`) and corrupt the set?
* Is `TObjectPtr` any safer than raw pointers as a *key*?
2. Performance / memory vs. `TArray`
* With only ~100 elements, is there any measurable benefit in using a hash container over a linear `TArray` + `FindByPredicate`?
* Are there engine heuristics that make `TSet`/`TMap` preferable even for such small counts?
3. Recommended best practices
* Does Epic suggest avoiding `TObjectPtr` as keys altogether?
* Should keys always be something immutable like `FObjectKey` or a GUID, and object pointers live as *values*, not keys?
* Any production patterns you can share for “cache of actors that may disappear at any time”?
4. TWeakObjectPtr
What is the official recommendation regarding using `TWeakObjectPtr` as a key in `TMap` or `TSet`?
We’ve observed that `TWeakObjectPtr`'s `operator==` treats any two invalid pointers as equal, even if their serial/index values differ. This can potentially lead to broken container invariants after GC, especially if two distinct weak pointers become invalid and end up treated as duplicates due to hash collisions.
Should we completely avoid using `TWeakObjectPtr` as a key?
---
I’ve read forum threads that hint at hash inconsistencies but couldn’t find an authoritative answer in the docs or code comments.
Thanks in advance for clarifying!
— Kirill Mintsev