LibGDX:How to create Key Value pairing holding info for different datatypes

Hi,

I would like to create something like a hashmap, but in my case I want the key to hold different values with different data types.
Or to be specific:

I want to appoint a KEY to a set of data like:

{
Author: String author;
Title: String title;
Year: Int year;
Sub-Title: String subTitle;
Image: Texture image;
Button: Texture button}

And then I want to be able to get the data by doing something like KEY.getImage / KEY.getAuthor etc.

I’m just not sure of how to do this (best). Can you point me in the right direction?

Is it essential that you design your own structure like this? Can you use a plain old hashmap? I.e. That data set looks like it could be representing a book. Is a HashMap<String, Book> insufficient?

I’m not sure because it’s the first time I’m working with this and I’m trying the wrap my head around how to do it.

What I want to do in I want to have a “level select” screen where I create a bunch of buttons depending on the amount of KEYS i have.
In this same screen I also want to get the Textures that are used as Images for these button from my “map”.
Then depending on which button I touched in my game screen I want to use that info to get the Right image depending on which key was paired up with that button and do something like key.getImage and get the Strings and Int that where paired up with that specific Key as well.

Since if i would want to do that using HashMap I should use something like a 2 step process of first creating a “book”-object (or whatever I/You want to call your object) like you suggest that would contain all the data I want to have, and consequently pair the objects up with keys in a HashMap. But before I create the objects then i would also like to automate the process of getting the values from the different data types into these object. So it already becomes a (min.) 3-step process.

So you see it’s already becoming a bit complicated and I’m not sure how to go about doing this.

You can simply create a new class and use in a hashmap, array, etc…

E.g: When you press a button:

...button change listener / Click listener
{
     Texture txt = object.getTexture (numberOfKeys);
}...
...public Texture getTexture(int numberOfKeys)
{
if (numberOfKeys > 5)
     return texture1;
else
     return texture2;
}..

It seems to me like you aren’t thinking with Objects. Like other people have said, you can just create a new class that holds that data:


public class Book{
   String author;
   String title;
   int year;
   String subTitle;
   Texture image;
   Texture button
}

Then you can create instances of that Book class to group your values together:


Book myBook = new Book();
myBook.author = "Dr. Seuss";
myBook.title = "Green Eggs and Ham";
myBook.year = 2015;
myBook.subTitle = "and other stories from the Necronomicon";

And then you can use those instances in whatever data structure you want, including a HashMap:


Map<String, Book> bookMap = new HashMap<String, Book>();
bookMap.put("key", myBook);

//...

String bookTitle = bookMap.get("key").title;

You can also update these Book instances after you add them to the Map:


bookMap.get("key").year = 2016;

I’m using the Book variables directly for simplicity, but real code would probably use constructors and getter/setter functions.

Thanks all.

In the meantime I wrote something myself as well which looks a bit different.
Seems to work as well, but not sure what would be the best way to go and why…


public class PaintingsHashMap {
	
	public static HashMap<String, String[]> paintingsHashMap;
	public static String[] milkmaid, house, italian;
	private Array<String[]> StringArrayCollection = new Array<String[]>();
	
	public PaintingsHashMap() {
		paintingsHashMap = new HashMap<String, String[]>();
		loadStringArrays();
		loadArray();
		keyValuePairing();
	}
		
	public void loadArray(){
		StringArrayCollection.add(milkmaid);
		StringArrayCollection.add(house);
		StringArrayCollection.add(italian);
	}

	public void keyValuePairing(){
		System.out.println("StringArrayCollection size = " + StringArrayCollection.size);
		for (int i = 0; i < StringArrayCollection.size; i++) {
			// key value pairing
			paintingsHashMap.put(StringArrayCollection.get(i)[0], StringArrayCollection.get(i));
			System.out.println(" key = " + StringArrayCollection.get(i)[0] + " , collection name = " + StringArrayCollection.get(i).toString());
					
		}
	}
			
	public void loadStringArrays(){
		
		// StringArray consist of Index 0 = KEY for HashMAP, 1 = Painter; 2 = Title 3= Sub Title; 
  // 4 = Year; 5 = FilePath Image Texture; 6 =  FilePath ButtonTexture 

		milkmaid = new String[]{"milkmaid","Johannes Vermeer", "The Milkmaid", "null", "1660", "", "select_screen/milkmaid.png"};
		italian = new String[]{"italian", "Hendrik Voogd", "Italian Landscape with Umbrella Pines", "null", "1807", "", "select_screen/italian_landscape.png"};
		house = new String[]{"house", "Willem Troost", "Front View of Buitenzorg Palace after the Earthquake of 10 October 1834", "null", "1834 - 1836", 
				"", "select_screen/house.png"};
		
	}

	public static String[] getStringArray(String key){
		return paintingsHashMap.get(key);
	}
}

Consequently I can load the button textures for my level select and “full” textures for my game screen like this f.e. :

import static com.norakomi.helpers.PaintingsHashMap.*;

...
...
etc.

button_italian = new Texture(getStringArray("italian")[6]);

Now I’m actually storing everything as a String instead of having each key paired up with different data type values…
Any additional/final thoughts?


edit: probably I can reduce the amount of code -specially if i have a large amount of String[]) - by “filling” StringArrayCollection something like this instead of creating the loadArray() and then I don’t have to explicitly declare the differnt String[] (…public static String[] milkmaid, house, italian;) :


StringArrayCollection.add(new String[]{"milkmaid","Johannes Vermeer", "The Milkmaid", "null", "1660", "", "select_screen/milkmaid.png"}
		);

That seems like a lot of work to just avoid creating a single class.

Is it really?

This is what my final (so far) class that takes care of pairing up all the data to KEYS looks like:


public class PaintingsHashMap {
	
	public static HashMap<String, String[]> paintingsHashMap;
	private Array<String[]> StringArrayCollection = new Array<String[]>();
	
	public PaintingsHashMap() {
		paintingsHashMap = new HashMap<String, String[]>();
		loadArray();
		keyValuePairing();
	}

	public void loadArray(){
		// StringArray consist of Index 0 = KEY for HashMAP, 1 = Painter; 2 = Title 3= Sub Title; 
		// 								4 = Year; 5 = Filepath painting texture; 6 = Filepath button texture
		
		StringArrayCollection.add(new String[]{"milkmaid","Johannes Vermeer", "The Milkmaid", "null", "1660", "",
				"select_screen/milkmaid.png"});
		StringArrayCollection.add(new String[]{"italian", "Hendrik Voogd", "Italian Landscape with Umbrella Pines",
				"null", "1807", "", "select_screen/italian_landscape.png"});
		StringArrayCollection.add(new String[]{"house", "Willem Troost", "Front View of Buitenzorg Palace after the Earthquake of 10 October 1834", "null", "1834 - 1836", 
				"", "select_screen/house.png"});
	}

	public void keyValuePairing(){
		for (int i = 0; i < StringArrayCollection.size; i++) {
			// key value pairing
			paintingsHashMap.put(StringArrayCollection.get(i)[0], StringArrayCollection.get(i));

// could also do something like:
// String key = StringArrayCollection.get(i)[0];		
// String value = StringArrayCollection.get(i);	
// paintingsHashMap.put(key, value);				
		}
	}
	
		*** I don't need the getStringArray() getter...
}

If I would create a Book (or Painting or whatever named) class I’d still need to create all the instances (comparable to the loadArray method), keyValue pair hem into a HashMap (comparable to my keyValuePairing method).

@Narokomi please just go with what @KevinWorkman suggested.

You’re using parallel arrays to keep track of your data, which is considered a pretty horrible approach by most people, for a lot of reasons.

It can be hard to “think in OOP”, but this is exactly what OOP is useful for.

Of course, it’s entirely up to you and what fits in your brain the best- but I think as you become more familiar with OOP, you’ll lose the parallel arrays and go with a simple Object.

@Norakomi: Yes, please listen to KevinWorkman.
Your code is horrible and very difficult to grasp/understand, let alone that with strings you also lose type-information for your attributes, such as the year being a string instead of a proper integer.
But you do not have to think OOP-ish to improve on that. Structural programming with a simple struct with typed members will also do, if that is more up your alley.
I mean, after all what you need is an associative data structure (a Map) which associates the keys to instances of a record type (type-theoretically speaking). And a record type in Java is a class.

Classes exist for exactly this purpose, but there is one potential Java-specific gotcha with KevinWorkman’s approach: (lack of) equality.
If you define a new class to use a record type in data structures (like a hashtable), then you should make sure the type properly implements/overrides equals() and hashCode().
For such simplistic and specific usage, you can probably get away with not doing it, but it’s important for general cases.

But yeah, that code hurts my soul.

Cool. tnx for all the feedback. I’ll be using Kevin’s code

I forgot to ask 1 more question, kind of essential when we’re talking about how to not code “horribly”.
Thing is i have about 50 of those “book” objects. That about 350 lines of code copy/pasted if I’d do it like this:


Book myBook1 = new Book();
myBook1.author = "Dr. Seuss";
myBook1.title = "Green Eggs and Ham";
myBook1.subTitle = "and other stories from the Necronomicon";
myBook1.year = 2015;
myBook1.buttonImage = .....;
myBook1.mainImage = ....;

Book myBook2 = new Book();
myBook2.author = "Dr. Seuss";
myBook2.title = "Green Eggs and Ham";
myBook2.subTitle = "and other stories from the Necronomicon";
myBook2.year = 2015;
myBook2.buttonImage = .....;
myBook2.mainImage = ....;

Book myBook3 = new Book();
myBook3.author = "Dr. Seuss";
myBook3.title = "Green Eggs and Ham";
myBook3.subTitle = "and other stories from the Necronomicon";
myBook3.year = 2015;
myBook3.buttonImage = .....;
myBook3.mainImage = ....;

etc etc... for all my 50 objects?

That’s why I initially tried to put all the info for one book (painting in my case…) into individual Arrays of Strings (only need ~50 lines of code) and then with a for loop a could automate the key value pairing for the HashMap.

So my question is using Kevin’s code how do you get all the info into 50 objects and still have great code (I assume you are not a big fan of copy/pasting it (7 lines of code * ) 50 times like the example above or are you)?

Load them from a file.

Make all the members inherit from some common base class (BookData maybe) and then flesh it out from there.

So basically,

public class Author extends BookData{

// all your fields, age, date of birth, etc

}

public class Book{

      // All the different data types
      List<BookData> data;


        public BookData getData<T>(){
            for(BookData d : data){
                if(d instanceof T)
                    return d;
                }
        }

}

So to use it you would do:

Author a = book.getData(Author)();

I might be a little off for the exact syntax there, hopefully someone can correct me if so :s.

Use json:


{
	"book_list": [
		{
			"autor": "Dr. Seuss",
			"title": "Dr. Seuss",
			"subtitle": "Dr. Seuss",
			"year": "Dr. Seuss",
			"button_image": "/path/to/image.png"
			"main_image": "/path/to/image.png"
		},
		{
			"autor": "Dr. Seuss",
			"title": "Dr. Seuss",
			"subtitle": "Dr. Seuss",
			"year": "Dr. Seuss",
			"button_image": "/path/to/image.png"
			"main_image": "/path/to/image.png"
		},
		{
			"autor": "Dr. Seuss",
			"title": "Dr. Seuss",
			"subtitle": "Dr. Seuss",
			"year": "Dr. Seuss",
			"button_image": "/path/to/image.png"
			"main_image": "/path/to/image.png"
		},
		{
			"autor": "Dr. Seuss",
			"title": "Dr. Seuss",
			"subtitle": "Dr. Seuss",
			"year": "Dr. Seuss",
			"button_image": "/path/to/image.png"
			"main_image": "/path/to/image.png"
		}
	]
}

Then somewhere in your java code:


Map<String, Book> books = new HashMap<String, Book>();

JSONObject json; // Parse json object

JSONArray jsonBooksList = (JSONArray) json.get("book_list");

for (int i = 0; i < jsonBooksList.size(); i++)
{
	JSONObject jsonBook = (JSONObject) jsonBooksList.get(i);

	String author = jsonBook.getString("author");
	String title = jsonBook.getString("title");
	String subtitle = jsonBook.getString("subtitle");
	int year = jsonBook.getInt("year");
	String buttonImage = jsonBook.getString("buttonImage");
	String mainImage = jsonBook.getString("mainImage");

	Book book = new Book(author, title, subtitle, year, buttonImage, mainImage);
	books.put(title, book);
}

The implementation of getting data from json objects will differ from json lib to json lib.

:thumbsup:

Yeah, though I’d prefer a json lib to automatically instantiate & populate Java objects (using reflection).
If you’re doing it properly, it should also have schema definition support. (though that seems a little beyond the scope of this thread)

Like I said in my post, you’d probably use a constructor in real code. Something like this:


public class Book{
   String author;
   String title;
   int year;
   String subTitle;
   Texture image;
   Texture button

   public Book(String author, String title, String subTitle, int year){
      this.author = author;
      this.title = title;
      this.year = year;
      this.subTitle = subTitle;
   }
}

Then you can do the initialization of each book in a single line:


Book myBook1 = new Book("Dr. Seuss", "Green Eggs and Ham", "and other stories from the Necronomicon", 2015);
Book myBook2 = new Book("Robert Kirkman", "The Walking Dead", "home", 2015);

This is pretty basic OOP, which is why I suggested you might want to go through a few basic tutorials before going further.