Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

[C#] Dictionary Loop

Discussion in 'Scripting' started by daybreaker93, Jul 2, 2015.

  1. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    I've just found out that using a foreach loop is bad (an extra GC alloc).
    I've got to do a loop on a dictionary on each update and currently using a foreach.
    I've found out that there is another way to do a loop on dictionary using for with elementAt by using System.Linq.

    the questions are:
    1. Why is it bad to use for + elementAt for dictionary loop?
    2. Can System.Linq be used by Net 2.0 subset?
    3. Is there any other way to do a dictionary loop without foreach?
    Still learning C#, Thank you.
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    is that extra gc going to completely break your game? if not it's really not worth worrying about it until you get towards the end of the project when you start optimising things.
     
    Kiwasi likes this.
  3. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    It's true but I hate to do major restructuring on multiple class later if I could avoid it by early optimizing.
    Oh, by the way, I'm working on a mobile game.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,335
    Linq has the same problems as foreach on Unity's mono compiler, as far as I am aware.

    The foreach isn't that bad, and since Dictionary doesn't have any indexed way to acces it's members, Linq has to use the same enumerator internally as foreach does.
     
  5. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    6,441
    Linq can be nice but a lot of times there is no gain over using it since it is basically going to do the same thing as a foreach in this case anyway. I try to use Linq when it improves readability and/or reduces nesting significantly.

    Why would you need to use elementAt in a loop? If you intend to actually loop through the dictionary then you would loop through all of the elements so the elementAt is pointless.

    Code (csharp):
    1.  
    2. foreach(var key in someDictionary.Keys) // loop through keys
    3. foreach(var value in someDictionary.Values) // loop through values
    4. foreach(KeyValuePair<K, V> p in someDictionary) // loop through both
    5.  
     
  6. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    I thought something like this
    Code (csharp):
    1. for (int i = 0; i < n; ++i) {
    2.     // do something with dictionary.ElementAt(i)
    3. }

    Oh my..
    So there are no other way?
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,335
    I guess you could implement your own Dictionary, where you have direct access to the key/value pairs, but I can guarantee that you won't be able to have it run as fast as the built-in one, as that's well optimized by a bunch of people.
     
  8. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    911
    I'd rather use the foreach, but you can do something like

    Code (csharp):
    1.  
    2.         Dictionary<string, string> dict = new Dictionary<string, string>()
    3.         {
    4.             { "A", "String A" },
    5.             { "B", "String B" },
    6.             { "C", "String C" },
    7.         };
    8.         string[] keys = new string[dict.Keys.Count];
    9.         dict.Keys.CopyTo(keys, 0);
    10.         for (int i = 0; i < keys.Length; ++i)
    11.         {
    12.             Debug.Log(dict[keys [i]]);
    13.         }
    14.  
    If you don't change the keys that often, you could just update the keys array whenever you update the dictionary.
     
    Zwergkrug likes this.
  9. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    6,441
    Why do this when you could iterate through the part of the kvp you want like the code I posted?
     
  10. Deleted User

    Deleted User

    Guest

    I think a KeyedCollection might also work.
     
  11. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    no. Doesnt solve anything.

    OrderedDictionary is what you want, however last I checked (which was pre-unity5) OrderedDictionary wasnt available.

    might pay to check if it is now, although im doubtful.
     
  12. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    I've checked on 5, I could use OrderedDictionary by using System.Collections.Specialized.
    By "wasn't available" did you mean it won't work on Net 2.0 Subset (after I build it on iOS)?

    Sorry, I should've mentioned that I currently am using foreach and wanted to know is there any other way to do dictionary loop without extra GC alloc.

    Hmm, what about this one?
    Any drawback?
     
  13. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    911
    The drawback is the CopyTo, which can be expensive. If you can cache the array, or your dictionary isn't that large you'll probably be OK.
     
  14. Deleted User

    Deleted User

    Guest

    This is the on proper way I know, no memory allocation, fast and simple enough.
    You could make enumerator typed but its type is so complicated, I guess we can forget it this one time

    Code (CSharp):
    1. var enumerator = my_dictionary.GetEnumerator();
    2. while( enumerator.MoveNext() )
    3. {
    4.     // Access value with enumerator.Current.Value;
    5. }
     
    daybreaker93 likes this.
  15. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    I didn't know about GetEnumerator + MoveNext method before you pointed one.
    I've done some research and found that GetEnumerator is better than foreach in here http://www.dotnetperls.com/dictionary-getenumerator

    Then I've done a little profiling with both method and found out that yes the GetEnumerator method didn't give an extra GC alloc.

    Thanks :)
     
    Novack and chrismarch like this.
  16. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    383
    How are you using your dictionary? Do you actually need to loop through its entirety each frame?
     
  17. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    At worst case yes.
    For multitouch handling without the new UI system.
     
  18. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    383
    Could you describe an example, maybe post some code in context?
     
  19. daybreaker93

    daybreaker93

    Joined:
    Jun 11, 2014
    Posts:
    71
    I'm really sorry, but it's like a super spaghetti code.
    Currently I'm doing some cleaning up and restructuring, so maybe I'll post it when it's clean.
     
  20. _watcher_

    _watcher_

    Joined:
    Nov 7, 2014
    Posts:
    132
    Unfortunately your first line of code:
    Code (CSharp):
    1. var enumerator = my_dictionary.GetEnumerator();
    still creates garbage. And since you need to call it every time the Dictionary size changes, you can't get a GC-free Dictionary..