data-structure

Managing nested structures in Java

Handling nested structures in Java is kind of a nightmare. For this reason I created EasyLoL.

LoL is abbreviation of List-of-List, a general way of calling nested structures.

Imagine having to build structure like that in pure Java, how many keystroke energy you need to waste. No more just use EasyLoL and you will have the last laugh …

a : >
  b : >
    c : ccc
    d : ddd
  f : >
    c : fff2
    e : [abra,cadabra]
bcd : bcd
abc : abc
structs : >
  list : [v1,v2,v3]
  hash : >
    k1 : v1
    k2 : v2
    k3 : v3

The module extends HashMap and builds on top of it.

It provides fetch(), set() and delete() methods that use a dotted syntax to access and instantiate an element anywhere in the hierarchy by autovivifing all the intermediary elements if necessary.

autovivify : is the automatic creation of new arrays and hashes as required every time an undefined value is dereferenced.

The module also support a quick way to create and insert on the fly a HashMap or ArrayList element, you do that by encoding it as string, pre-pending it with special character, so that the module can figure out which one do you want to create.
Here is an example :

//hash in a string
e.set("structs.hash", ">k1:v1,k2:v2,k3:v3");
// list in a string
e.set("structs.list", "]v1,v2,v3");	

Below you can see the class and examples of how to use it.
Also you can also use dump() function I described in a previous post to pretty print the data structure.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// Easy way to Create and Access hierarchical structures
public class EasyLoL extends HashMap<String, Object> {
	
		public static <ARG> void say(ARG arg) { System.out.println(arg); }

		//extract value by going trough the hierarchy
		private Object _by_key(String[] keys, HashMap val) {
			Object rv = null;
			if ( val.containsKey(keys[0])) { rv = val.get(keys[0]); }
			else {
				//log.debug("! by_key> no such key : " + keys[0]);
				return null; 
			}
			// dig deeper, (skim the first element of String keys)
			if (rv instanceof HashMap && keys.length > 1 ) return _by_key(Arrays.copyOfRange(keys, 1, keys.length), (HashMap) rv);
			if (keys.length > 1) return null; //still more keys, but no more data that deep 
			return rv;
		}
		
		//helper method for LoL objects access
		public Object by_key(String key) {
			String[] keys = key.split("\\.");
			return _by_key(keys, this);
		}
		
		//Getter methods ....
		public Object find(String key) { return key.contains(".") ? by_key(key) : get(key); }
			
		public String gets(String key) {
			Object obj = find(key);
			if (obj instanceof String) return (String) obj;
			if (obj == null) return null;
			return obj.toString();
	 	}
		
		public void delete(String key) {
			int i = key.lastIndexOf('.') ;
			if (i == -1) return;
			String upto = key.substring(0,i);
			String last = key.substring(i+1);
			EasyLoL prev = fetch(upto);
			prev.remove(last);
		}
		
		public <T> T fetch(String key) {
			Object obj = find(key);
			if (obj == null) return null;
			return (T) obj;
	 	}
		
		public ArrayList by_keys(String ... keys) {
			ArrayList<String> lst = new ArrayList<>();
			for (String key : keys) lst.add(gets(key));
			return lst;
		}
		
		//converts string to Hash
		public HashMap<String,String> str2lol(String str) {
			HashMap<String,String> hash = new HashMap<String,String>();
			String[] kvs = str.split("[,:]");
			for (int i=0; i < kvs.length; i += 2) hash.put(kvs[i], kvs[i+1]);
			return hash;
		}
		
		//handles setting complex types
		private <T> void insert(EasyLoL lol, String k, T value) {
			//hash encoded in string
			if (value instanceof String) {
				if (((String) value).startsWith(">")) {
					HashMap<String,String> val = str2lol( ((String) value).substring(1) );
					lol.put(k, val);
					return;
				} else if (((String) value).startsWith("]")) {
					String val = ((String) value).substring(1);
					lol.put(k, Arrays.asList(val.split(",")));
					return;
				}
			} 
			lol.put(k, value);			
		}
		
		public <T> void set(String key, T value) throws Exception { this.set(key, value, true); }
		
		public <T> void set(String key, T value, boolean silent) throws Exception {
			
			//no hierarchy
//			if (! (key.contains(".") && this.containsKey(key))) {
//				this.put(key, value);
//			}
			
			String[] keys = key.split("\\.");
			//start point
			EasyLoL lol = this;
			int pos = 0;//are we at a leaf 
			//incrementally vivify the keys
			for (String k : keys) {
				if (lol.containsKey(k)) {
					if (lol.get(k) instanceof Map) {
						lol = (EasyLoL) lol.get(k);//extend
					} else {
						if (pos >= keys.length-1) {
							insert(LoL,k,value);//overwrite
							return;
						}	
						if (silent) return;
						throw new Exception("using : " + key + " ! Can't overwrite the structure at sub-key : " + k);
					}
				} else {//autovivify
					if (pos < keys.length-1) {//Intermediary keys
						LoL.put(k, new EasyLoL());
						//switch/move one level down
						LoL = (EasyLoL) LoL.get(k);
					} else {//tree leaf, set the value
						insert(LoL,k,value);
					}
				}
				pos++;
			}
	 	}
		
		
	public static void main(String[] args) throws Exception {
		EasyLoL e = new EasyLoL();
		e.set("abc", "abc");
		//Hierarchical set
		e.set("a.b.c", "ccc");
		e.set("a.b.d", "ddd");

		e.set("a.f.c", "fff");
		//overwrite
		e.set("a.f.c", "fff2");
		//conflict : structure already in place
		e.set("a.f.c.x", "fffx");//silent
		//e.set("a.f.c.x", "fffx",false);//throw error
		
		//setting java structure
		ArrayList ary = new ArrayList() {{
			add("abra");
			add("cadabra");
		}};
		e.set("a.f.e", ary);
		
		e.set("bcd", "bcd");
		//hash in a string
		e.set("structs.hash", ">k1:v1,k2:v2,k3:v3");
		// list in a string
		e.set("structs.list", "]v1,v2,v3");
		
		say("------------------------------------------");
		say("a.b : " + e.fetch("a.b"));
		say("a.b.c : " + e.fetch("a.b.c"));
		say("structs.list : " + e.fetch("structs.list"));
		say("structs.hash : " + e.fetch("structs.hash"));
//		e.delete("a.b");
		say("==========================================");
//		say(utils.dump(e));
	}

}

------------------------------------------
a.b : {c=ccc, d=ddd}
a.b.c : ccc
structs.list : [v1, v2, v3]
structs.hash : {k1=v1, k2=v2, k3=v3}
==========================================
a : >
  b : >
    c : ccc
    d : ddd
  f : >
    c : fff2
    e : [abra,cadabra]
bcd : bcd
abc : abc
structs : >
  list : [v1,v2,v3]
  hash : >
    k1 : v1
    k2 : v2
    k3 : v3

Get the size of data structures in Python

If you want to find or measure the size of memory a data structure or objects occupies, try the function below.

import sys
    
def get_size(obj, seen=None):
    """Recursively finds size of objects"""
    size = sys.getsizeof(obj)
    if seen is None:
        seen = set()
    obj_id = id(obj)
    if obj_id in seen:
        return 0
    # Important mark as seen *before* entering recursion to gracefully handle
    # self-referential objects
    seen.add(obj_id)
    if isinstance(obj, dict):
        size += sum([get_size(v, seen) for v in obj.values()])
        size += sum([get_size(k, seen) for k in obj.keys()])
    elif hasattr(obj, '__dict__'):
        size += get_size(obj.__dict__, seen)
    elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
        size += sum([get_size(i, seen) for i in obj])
    return size
    

print(get_size(list(range(100))))
print(get_size(list(range(1000))))
print(get_size([[ i for i in range(10)] for j in range(100)]))
print(get_size([[ i for i in range(100)] for j in range(10)]))


-----
3804
37108
20388
12108