questions about choosing suitable data structure in Java - java

My problem is that there are 3 variables, say a,b,c
First, information a,b,c needed to be input and stored.
For example a=Tom, b=school A, c=1402
Then, I can get the above information by entering "Tom", in this case, if there is another Tom in school B,that message would also be printed out.
or by entering "Tom, school A", then only that Tom would be printed out. In this problem, their would be no duplicates if a and b are both locked.
Well, my view is that a must be a key, and a+b can be a composite key. So firstly I thought of a HashMap, however, HashMap has only one key to one value. So I intended to use 2 maps but this could not make use of composite key.
So are my thoughts correct? Or are there any better data collections in Java could be used in this case?
Thank for your time!

There is no off the shelf data structure in Java for your need. There are multi valued map implementations in Apache Commons and Google Guava Collections but not in core Java.
But , In my opinion, you can implement it with a map declaration like - Map<String, List<String>> or Map<String, List<Student>> depending on whether you are keeping your student details as concatenated strings or as a Student class.
While populating the map - you will make two entries in map for first row / student , once for a and another entry for a+b concatenated.
Then while making entries for subsequent rows , you should first check if key exists in map or not, if it exists, you get the value , append new value and store again.
Record a=Tom, b=school A, c=1402 will have two entries then comes record a=Tom, b=school B, c=1403 and you will append record for key = Tom and add a new entry for TomschoolB so in total three entries in map for keys - Tom , TomschoolA and TomschoolB. Key Tom will have two items in value list while rest two will have single items.
Your lookup code should follow key calculation logic as key creation logic i.e. string concatenation or any other things as you wish.
This is just give a rough idea that multiple values can be handled via list.
Edit : From your comments, you look confused so below is code to implement the idea. You can tweak and extend it as you wish.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MultiValuedMap {
private static Map<String,List<String>> storage = new HashMap<>();
public static void main(String[] args) {
store("Tom","Tom,schoolA,1402");
store("TomschoolA","Tom,schoolA,1402");
store("Tom","Tom,schoolB,1402");
store("TomschoolB","Tom,schoolB,1403");
storage.forEach((key,value) -> System.out.println("key:"+key+",value:"+value));
}
private static void store(String key, String value){
if(storage.containsKey(key)) {
List<String> newList = storage.get(key);
newList.add(value);
storage.put(key,newList);
}else{
List<String> values = new ArrayList<String>();
values.add(value);
storage.put(key,values);
}
}
}
Output:
key:Tom,value:[Tom,schoolA,1402, Tom,schoolB,1402]
key:TomschoolB,value:[Tom,schoolB,1403]
key:TomschoolA,value:[Tom,schoolA,1402]

When you feel ambiguous of using any existing datastructure I suggest you to create your own data structure.With that you can create any methods and anything according to your need.

Related

How to return a key content from a HashMap in java?

Working on a small project which stores some students' records in a HashMap as database.
private HashMap<String, Student> studentDb = new HashMap<String, Student>();
My application has a UI which lets the users to choose any string as key record. It can be numbers or letters.Creating each record, creates an objects from another class which keeps information in its fields, but it is not suppose to store the key.
After storing all the data they can request to see all the data has been stored so far. The toSting method from the class which I instantiate my object from, generates the message to be displayed.
At the time of displaying, I need to display the key they chose with the rest of stored data and they may not ask for all of them. The question is, how can I get access to a given record's key? Let's say if I don't want to use key that user should enter at the time of their request. Is there any way to drive it from the HashMap collection?
I hope I could explain my question.
Thank you.
Edit:
This is the output for a request, but the thing is I read the student's Id from the textfield they insert the request which is kind of cheating. I wondered if I can drive it from the collection itself.
Could use something like this:
Working version:
import java.util.HashMap;
public class test{
public static void main(String[] args){
HashMap<String, String> studentDb = new HashMap<String, String>();
studentDb.put("1","A");
studentDb.put("2","B");
studentDb.put("3","C");
for(String key : studentDb.keySet()){
System.out.println(studentDb.get(key));
}
}
}
What you could do for your situation:
...
for(String key : studentDb.keySet()){
if(studentDb.get(key)==/* STUDENT OBJECT */){
// Display / do something with the key.
}
}
...
What do you mean "to drive it from the HashMap collection"?
Your hashmap stores data by key (in this case a String). In order to access a specific value, you need to know its key. If I understand correctly, what you want would be like doing something like :
map.get("myKey").getKey();
Which doesn't make sense.
What you can do is iterate over all the keys in your map, with the keySet() method. But I'm not sure that's what you were looking for.

How to create a Vector where each entry is a structure with 2 fields

I am trying to create a data structure in Java that is a Vector into which some information about an unknown number of entities will go from a database. When it comes to this information, I only care about 2 fields. Also, it is required that when I iterate through this Vector, I can extract these two fields (say, String) in pairs. Schematically speaking,
String s1 = Vector[1].Field1, String s2 = Vector[1].Field2
Is this even possible? Does anyone know a more efficient way to achieve this?
Note: I would like to keep it in a single Vector because I pass it to another class for processing.
Use
public class Entry {
public String field1;
public String field2;
}
List<Entry> vector = ...;
Why not using Map<String, String>
But map will be unique keys
Also you can create your own Class and pass it to the Vector<yourclass>
Here's how I would do it :
Create an object Pair with 2 attributes : field 1, field 2
Add this object to your vector.
Use a Vector of maps, with keys value1 and value2. Vector<Map<String, String>>.
Or define a new object with two attributes ( I suggest the second option )

Most efficient way to split one arraylist data into 4 seperate arraylists?

I am currently writing code which contains an arraylist. This arraylist includes data which is name, lastname, job and id. I need to seperate the data into different arraylists. Currently i am using the method which is shown below.
for (int i = 0; i < details.size(); i = i + 4) {
names.add(details.get(i));
lastname.add(details.get(i + 1));
job.add(details.get(i + 2));
id.add(details.get(i+3));
}
I want to know if there is a better way of doing this. The initial arraylist can be very long, and i dont know if there are any issues with this method.
You asked: "I want to know if there is a better way of doing this". There is a better way.
You should consider creating a class called Record that contains the data (name, last name, job, and ID), and create an ArrayList. Then, instead of using index locations (and potentially grab the wrong data item), you could use the Record getter methods to get the data item you need (and perhaps store it in a different list).
Step 1: Create a Record class:
public class Record
{
private String firstName;
private String lastName;
private String job;
private String id;
// TODO add constructor(s), getters and setters
}
Step 2: Create a list of Records (this is an better alternative that create a list having the information in different index locations. That way, each set of name, last name, job, and ID will be self-contained which is way better than disjointed in different index locations in a list.
ArrayList<Record> records = new ArrayList<Record>();
Step 3: Instead of using index locations (and potentially grab the wrong data item), you could use the Record getter methods to get the data item you need (and perhaps store it in a different list).
ArrayList<String> names = new ArrayList<String>();
ArrayList<String> jobs = new ArrayList<String>();
...
names.add(records.getLastName() + ", " + records.getFirstName());
jobs.add(records.getJob());
Alternatively, and maybe a better solution, you could use a Map to store this information. For example, a job ID could be the key in a Map that returns a job description and who has been assigned to perform it. Job IDs have to be unique. Adding IDs to a list can be duplicated, because the List interface doesn't restrict entering duplicate data. If you use a Map, they keys are guaranteed to be unique. The value being returned from the Map could be a Record object (or some other kind) that contains the name of the person and the job the person is responsible for. Since values can be duplicates, you can have a person performing multiple jobs, which is probably what you want to do. To use a Map:
Map<String, Record> jobs = new HashMap<String, Record>(); //This record class doesn't have ID in it.
jobs.put("ABC123", new Record("John", "Doe", "Fix Drywall");
jobs.put("321CBA", new Record("Bill", "Smith", "Install Light Fixtures");
A few things to consider if using a Map. If you try to make a new entry with an existing key, the old one will be overwritten.
jobs.put("ABC123", new Record("John", "Doe", "Fix Drywall");
jobs.put("ABC123", new Record("Bill", "Smith", "Install Light Fixtures"); //Overwrote the previous entry because key is the same
If you want to change the key for an existing value, you must obtain the value, store temporarily, remove the old record, and make a new entry with the old temp value:
jobs.put("ABC123", new Record("John", "Doe", "Fix Drywall");
Record rec = jobs.remove("ABC123"); // gets the record and removes old entry
jobs.put("321CBA", rec); // new job ID for old record
The main issue is that your details can have missing data. For example it has the size=5. Then your method will crush with IndexOutOfBounds. Your details list should contain a Person object which has all the details you want and then just use them to fill other lists.
The main performance kill will be the add operation since it will have to grow the data structure over time. Since you know details.size() you should initialize the other arraylists with details.size()/4.
You should also check that details.size() % 4 == 0 before the for loop. If not it means your data is somehow wrong and you will run for sure into an IndexOutOfBounds.
Just for correctness you should write i < details.size()+3 as your condition, since you will access element i+3 in the for body. You should always check for i < details.size()+x do it like this if you ever access i+x in the body. (for the largest x there will be in the body)

Get element without iterating

I have an ArrayList of HashMap key-value pairs which looks like
ArrayList<HashMap<String, String>> myList =
new ArrayList<HashMap<String, String>>();
I understand that I can iterate through these items and find a match, but this seems to be an expensive task. Is there any other way to get an element without iterating?
My ArrayList has values like
[{Father Name=a, Mother Name=b, Child Name=c, Reg No=1, Tag ID=1},
{Father Name=p, Mother Name=q, Child Name=r, Reg No=2, Tag ID=2},
{Father Name=x, Mother Name=y, Child Name=z, Reg No=3, Tag ID=3}]
Based on RegNo, I wish to get Father Name, Mother Name and Child Name without iterating individual items.
Without iterating you will need to store your HashMap in another HashMap with key Reg No. Though I'd recommend using a Family object or something similar: HashMap<Integer, Family> registration (that's the beauty of OO-languages :) )
class Family {
String father;
String mother;
String child;
// constructor getters setters
}
Map<Integer, Family> registration = new HashMap(); // note this is a JDK7 future
//Map<Integer, Family> registration = new HashMap<Integer, Family>(); // the 'old' way
registration.put(regNo, new Family("Jack", "Mary", "Bastard"));
Family family = registration.get(regNo);
String father = family.getFather();
since you are storing hashes in list, that means order remain constant. So that mean you can create another array to store the Reg No in same order, and then search reg no in that array and based on searched value index you can get the other values.
Iterating is O(n), but you want the access to your structure to be faster... This means storing objects in a ordered manner ( -> O(log(n)) usually) or using another hash ( -> O(1)).
Or this, or you "hide" the iteration, but this would solve the problem only esthetically (something like getElementsByTagName in xml).
In any case you'll probably have to alter your structures, especially if you want to be able to have faster access for every field (father/mother/child/tag) and not just 'reg no'.
Maybe another solution could be storing plain data in a hash with a keypair like (primary key, data), duplicating the PK for every field in your HashMap, but this not only implies searching a valid primary key, there could be the problem of the size of the hash.

Is it possible to have a hashmap with 4 objects?

Can I have an hashMap with say ID as my key and info, name, quantity as my values?
ok, say I have a class (Products) already that sets my variables, getters and setters. In my Invoice class, which is where the hashMap would be. Would I put like:
private HashMap<String, Products> keys = new HashMap<String, Products>
I'm not quite sure how to access the HashMap though. Say I implement a class that allows me to add and remove invoices from the HashMap, I do not know what the values would be:
keys.put(??value of id??,??not sure what goes here??);
Sure. Make another class that contains your info, name and quantity and put that as the value of your HashMap.
No, but the best way is to wrap the information you want to keep in the map in a class:
public class Info {
private String info;
private String name;
private int quantity;
...
public Info(String info, String name, int quantity) {
...
}
}
Then do this to put something in the map:
Info info = new Info("info", "name", 2);
Map map = new HashMap<Integer, Info>();
map.put(22, info);
And do this to get something out:
Info info = map.get(22)
How about HashMap<Integer, ArrayList<String>> ?
UPDATE: Please try to avoid this, this is a better approach.
Not exactly.
A Map defines a strictly 1 to 1 relationship between keys and values. One key in the map has one value.
If you want to associate multiple values with one key you need to do one of the following:
Define a Values class to represent the values as a single object; e.g. as per #Starkey's and #Javed's answers. Then the map becomes a Map<String, Values> (assuming that the key type is String).
Define the map as a Map<String,List<Object>> or Map<String,Object[]> and represent the values as an untyped list / array
Define the map as a Map<String,Properties> or Map<String,Map<String,Object>> and represent the values as the Java equivalent of an associative array.
Of these, the first option is both the safest (smallest chance of runtime errors), the most efficient and the best style.
(Aside: an Apache commons MultiMap might be considered as another possibility, but the conceptual model and APIs don't really match this use-case.)
Sure. Depending on how flexible your datastructe is you can use a Hashmap a la:
HashMap<IdType, List<String>>, with IdType String or Integer, depending on the Keys you like to use.
HashMap<IdType, String[]>
HashMap<IdType, YourObjectType>, with YourObjectType beeing a Object you defined yourself, holding the values you like
YourObjectType can of course be anything you can define as an Object. Also another HashMap if you like.
One of the concerns while using a Map would be use of hardcoded keys. If the key is a string, and the key changes. Can consider using a constant instead of a hardcoded string.
Having a dedicated class has the benefit of compiler to check for name changes. However, as mentioned in the earlier comments.. It can become a concern...
In my opinion both are feasible. We need to weigh which option is better depending on the situation
Create an object that encapsulates the four together. Something like:
public class Foo {
private String s1;
private String s2;
private int v3;
private MyObject obj1
// constructors, getters, helper functions.
}
I think MultiMap from google library could serve the purpose
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html
Multimap<String, String> map = ArrayListMultimap.create();
String key = "uniqueKey";
map.put(key, "value1");
map.put(key, "value2");
map.put(key, "value3");
System.out.println(map);//{uniqueKey=[value1, value2, value3]}
Of course, you could for example declare it like this: HashMap<Integer, HashMap<String,Object>> You use the outer hashmap to link your id with your inner HashMap, and in the inner one, you create keys "info", "name", "quantity" and associate values with them.
Of course, you could also use an ArrayList as the outer collection (it could be a better match for your ID: ArrayList<HashMap<String,Object>> that way you have indexed (id based) access to each of your "info", "name", "quantity" hashmap "records"
You could have ID as key and a List or Set (Collection in general) of objects as value.

Categories

Resources