1

In Java, a large 2D array with sparse contents is rather memory inefficient, as it takes up a contiguous chunk of memory regardless of what data is actually stored in it.

I am looking for an alternative data structure that can hold elements in a positional, grid-like, way. I would preferably like the memory efficiency to be O(n) where n is the number of data elements actually in the structure.

Cheers!

bhspencer
  • 11,362
  • 4
  • 29
  • 38
  • 2
    How about a map whose key is a POJO with a pair of indexes? (This might not be best if you need to return entire rows, but if all you're doing is accessing individual elements it should work fine.) – ajb Aug 28 '17 at 13:32
  • this? https://stackoverflow.com/questions/45829943/how-to-efficiently-store-small-byte-arrays-in-java I think just recently this was discussed – Eugene Aug 28 '17 at 13:33
  • 1
    If you don't want to implement a new class for the key to your Map you could just use a String of the form "A_B" where A is the index into the first dimension and B is the index into the second. – bhspencer Aug 28 '17 at 13:35

2 Answers2

2

You can use a HashMap where the key contains the X and Y position. If you don't want to implement a new class for the key to your Map you could just use a String of the form "X_Y" where X is the index into the first dimension and Y is the index into the second.

Map<String, YourClass> map = new HashMap<>();

To put an item at position 2345, 6789, you would do this:

map.put(2345 + "_" + 6789, yourObject);

And to get it back:

YourClass yourObject = map.get(2345 + "_" + 6789);

Why choose this method over using some kind of Tuple? I think the short answer is that Java doesn't come with a Tuple or Pair class. You could write your own and add your own hashCode() and equals() implementation or you could use something like Map.Entry<K, V>. Whether you choose a String for your key or a class for your key will make no significant difference to execution time. You loose some type safety by using a String and you make up for that with some brevity of code.

bhspencer
  • 11,362
  • 4
  • 29
  • 38
  • 1
    This would create new `StringBuilder` each time you added new entries to the map, finding "neighboring" entries would also require parsing the key resulting in more `String` objects, and it lacks type safety - why use a `String` for this? – Dioxin Aug 28 '17 at 13:44
  • Does java String literal concatenation really create a StringBuilder behind the scenes? That seems rather unlikely. I propose using a simple String literal as it is much less work than using hashCode() and equals() on some other class that you implement your self. – bhspencer Aug 28 '17 at 13:48
  • This answer suggests that a StringBuilder is not used for String literal concatenation https://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java – bhspencer Aug 28 '17 at 13:49
  • That"s exactly what string concatenation does. That answer doesn't suggest `StringBuilder` is not used - check the bytecode. – Dioxin Aug 28 '17 at 13:50
  • @VinceEmigh do you have anything to back that claim up? – bhspencer Aug 28 '17 at 13:51
  • Read the post you linked... "*Version 1 is preferable because it is shorter and the compiler will in fact turn it into version 2*" – Dioxin Aug 28 '17 at 13:52
  • 1
    *"I propose using a simple String literal as it is much less work than using hashCode() and equals() on some other class that you implement your self."* Writing `hashCode` and `equals` for a pair class takes like two minutes, at most. – Radiodef Aug 28 '17 at 13:53
  • String concatenation of literals does not use a StringBuilder. However, if some of the strings are non-literal, then it typically does. (If you want evidence, write some simple Java methods, compile them, and look at the bytecodes using the `javap` utility. Or just look at the JLS!) – Stephen C Aug 28 '17 at 13:54
  • Generating hashcode / equals takes me about 5 seconds in Eclipse. Less time than it took to write this comment :-) – Stephen C Aug 28 '17 at 13:55
  • Which ever way you cut it you are going to have some overhead generating the key. Be that either creating an instance of some kind of Pair or concat some String. There is also going to be some overhead running the hashCode(). This all comes at the benefit of saving space and having O(n) lookup. – bhspencer Aug 28 '17 at 14:01
2

Try a Map or Dictionary of Tuple(X,Y)

If you want linear range access (for all X give me range of Ys) then containers that are implemented with trees may still work ok. Hash tables will be good for random access.

Also try SparseArray

No matter what your choice, make sure to:

  1. Understand the underlying implementation, although it potentially could change over time.
  2. Measure and compare

Measuring usually beats Theorizing

codenheim
  • 19,092
  • 1
  • 51
  • 77
  • Which particular concrete class are you suggesting using for the key? Are you proposing the OP create a new Tuple class for their key and implement hashCode()? – bhspencer Aug 28 '17 at 13:44
  • @bhspencer Could use `Map.Entry`, although creating a new type for something like this definitely isn't unheard of, seeing how Java lacks a `Pair` type – Dioxin Aug 28 '17 at 13:45
  • For a simple implementation you could also use a `String` in the format `"X:Y"` (for example) – vikingsteve Aug 28 '17 at 14:06