I always loved the ease of use of arrays in PHP. Simple, dynamic arrays; arrays with integer indexes or string keys; hash tables, maps, collections, resizable variant arrays; you name it.

In C++, you can refer to Standard Template Library (STL) and use vectors for example if you need a regular dynamically sized array, integers used as keys; or maps if you need to use strings as keys.

In C#, namespace Collections contains similar classes to achieve same results; where a C++ vector is a List or ArrayList, and a map is a Hashtable or Dictionary.

PHP has also similar library called Standard PHP Library (SPL).

Java Collections Framework does the same job with lists and maps.

For common problems, common solutions are provided in these libraries.

using System;
using System.Collections;
using System.Collections.Generic;

namespace MyNamespace
{
    class CAssocArray : System.Collections.IEnumerable
    {
        public const bool USE_INT_AUTOINDEX = true;
        public const int INDEX_TYPE_INT = 0;
        public const int INDEX_TYPE_STRING = 1;

        public enum INDEX_TYPE {INTEGER, STRING};

        private const int nil = -9999999;
        private int my_max_key;//my_min_key, my_first_key, my_last_key;
        
        private Dictionary d;

        //if true, it is like 0-based normal array, sequential numbers will be used as keys
        private bool isIntAutoIndexed;
        private INDEX_TYPE index_type;

        public CAssocArray()
        {
            this.init(0, true);
        }

        public CAssocArray(CAssocArray a)
        {
            this.init(a.index_type, a.isIntAutoIndexed);
            foreach (int k in a.d.Keys)
            {
                //Object o = a.d[k]; T t = (T) o; this.d[k] = t.MemberwiseClone();
                this.d[k] = a.d[k];
            }
        }

        public CAssocArray(INDEX_TYPE index_type, bool isIntAutoIndexed)
        {
            this.init(index_type, isIntAutoIndexed);
        }

        private void init(INDEX_TYPE index_type, bool isIntAutoIndexed)
        {
            my_max_key = nil;//my_first_key = nil; my_last_key = nil;
            d = new Dictionary();
            this.index_type = index_type;
            this.isIntAutoIndexed = isIntAutoIndexed;
        }
        public bool isIndexType_Int()
        {
            return this.index_type == CAssocArray.INDEX_TYPE.INTEGER;//INDEX_TYPE_INT;
        }
        public bool isIndexType_String()
        {
            return this.index_type == CAssocArray.INDEX_TYPE.STRING;//INDEX_TYPE_STRING;
        }
        public int size()//alias for c++ "vector" class
        {
            return d.Count;
        }

        public int Length()
        {
            return d.Count;
        }

        public int Count()
        {
            return d.Count;
        }

        public void push_back(T value)//alias for c++ "vector" class
        {
            this.insert(value);
        }

        /**
         * Insert value, use the next number in the sequence as key
         */
        public void insert(T value)
        {
            if (my_max_key == nil) my_max_key = -1;
            int key = (my_max_key + 1);
            //string skey = key.ToString();
            this.insert(key, value);
        }

        public void insert(string strKey, T value)
        {
            int key = strKey.GetHashCode();
            this.insert(key, value);
        }

        public void insert(int key, T value)
        {
            d.Add(key, value);
            if (key > my_max_key) my_max_key = key;
        }


        public bool remove(int key)
        {
            bool r = false;
            if (key != nil)
            {
                if (d.ContainsKey(key))
                {
                    r = d.Remove(key);
                    if (key == my_max_key) my_max_key = this.getMaxKey();
                }
            }
            return r;
        }

        public bool clear()
        {
            d.Clear();
            my_max_key = nil;
            return true;
        }

        public bool removeFirst()
        {
            int key = this.getFirstKey();
            return this.remove(key);
        }

        public bool removeLast()
        {
            int key = this.getLastKey();
            return this.remove(key);
        }

        public bool pop_back()
        {
            return this.removeLast();
        }

        public List getKeyList()
        {
            return d.Keys.ToList();
        }
        public List keys()
        {
            return d.Keys.ToList();
        }

        public List getValueList()
        {
            return d.Values.ToList();
        }

        public List list()
        {
            return d.Values.ToList();
        }

        public List values()
        {
            return d.Values.ToList();
        }
				
        public IEnumerator GetEnumerator()
        {
            List list = this.d.Values.ToList();
            foreach (T item in list)
            {
                yield return item;
            }
        }
				
        private int getLastKey()
        {
            List l = this.getKeyList();
            if (l.Count == 0) return nil;
            return l.Last();
        }

        private int getFirstKey()
        {
            List l = this.getKeyList();
            if (l.Count == 0) return nil;
            return l.First();
        }
				
        private int getMaxKey()
        {
            List l = this.getKeyList();
            if (l.Count == 0) return nil;
            return l.Max();
        }

        public T first()
        {
            int key = this.getFirstKey();
            return this.get(key);
        }

        public T last()
        {
            int key = this.getLastKey();
            return this.get(key);
        }

        public T this[int key]
        {
            get
            {
                return this.getI(key);
            }
            set
            {
                this.setI(key, value);
            }
        }

        public T this[string key]
        {
            get
            {
                return this.getS(key);
            }
            set
            {
                this.setS(key, value);
            }
        }


        private T get(int key)//HashCode
        {
            T t = default(T);
            if (d.ContainsKey(key)) t = d[key];
            return t;
        }

        private T getI(int key)
        {
            int Hkey = this.isIntAutoIndexed ? key : key.ToString().GetHashCode();
            return this.get(Hkey);
        }

        private T getS(string key)
        {
            int Hkey = key.ToString().GetHashCode();
            return this.get(Hkey);
        }

        private void set(int key, T value)//HashCode, value
        {
            if (d.ContainsKey(key))
                d[key] = value;
            else
                this.insert(key, value);
        }

        private void setI(int key, T value)
        {
            int Hkey = this.isIndexType_Int() ? key : key.ToString().GetHashCode();
            this.set(Hkey, value);
        }

        private void setS(string key, T value)
        {
            int Hkey = key.GetHashCode();
            this.set(Hkey, value);
        }

    }//end class

}//end namespace

//Now, how we can use this class:

namespace MyNamespace
{
	class Edge
	{
		public int target;
		public int input;
		public double probability;
		
		public Edge(int target, int input, double probability)
		{
			this.target = target;
			this.input = input;
			this.probability = probability;
		}
	}

	class Edges : CAssocArray
	{

		public Edge getClassWithMaxProbability()
		{
			double max_prob = 0.0; int max_idx = 0;
			int n = this.size();
			Edge e = new Edge();
			if (n == 0)
			{
				return e;
			}
			foreach (int i in this.getKeyList())
			{
				Edge ei = this[i];
				if (max_prob < ei.probability)
				{
					max_prob = ei.probability;
					max_idx = ei.target;
					e = ei;
				}
			}
			return e;
		}
	}

	class Node
	{
		public double probability;
		public Edges edges;

		public Node(double probability)
		{
			this.probability = probability;
			this.edges = new Edges();
		}
	}

	class Nodes : CAssocArray
	{
		public Nodes()
		{
			//do something
		}
	}

	class Program{

		static void Main(string[] args)
		{
			Nodes nodes = new Nodes();
			Node node0 = new Node(1.0);
			Node node1 = new Node(0.0);
			Node node2 = new Node(0.0);
			Node node3 = new Node(0.0);
			nodes.insert(node0);
			nodes[1] = node1;
			nodes.insert(node2);
			nodes[3] = node3;
			
			node0.edges.insert(new Edge(1, 11, 0.5));
			node0.edges.insert(new Edge(2, 12, 0.5));
			

			node1.edges[2] = new Edge(2, 12, 0.25);
			node1.edges[3] = new Edge(3, 13, 0.75);
			
			node2.edges[1] = new Edge(1, 11, 0.75);
			node2.edges[3] = new Edge(3, 13, 0.25);
			
			node3.edges[0] = new Edge(0, 10, 1.0);
			
			nodes[0].probability -= 0.25;
			nodes[1].probability += 0.25;
			
			nodes[0].edges[0].probability -= 0.1;
			nodes[0].edges[1].probability += 0.1;
			
			nodes[3].edges[0].probability = 0.5;
			nodes[3].edges[1] = new Edge(1, 11, 0.5);

			foreach(int i in nodes.getKeyList())
			{
				Node node = nodes[i];
				foreach(int j in node.edges.getKeyList())
				{
					Edge edge = node.edges[j];
					//do something
				}
			}

		}

	}

}//end namespace

What would it look like? This is a simple state machine with some nodes and edges linking nodes to each other.

State Machine