How to create a Hashtable using a method as a struct - java

My overall goal is to fill a Hashtable with employee data and be able to access/modify that data then print it out.
Given my Employee class here, can I use it like a struct in C? I'm trying to figure out how to initialize a Hashtable and fill it with this data but I'm sort of confused on how to implement it.
public class Employee {
private String empName;
private int empNum;
private String empJob;
public Employee(String empName, int empNum, String empJob)
{
this.empName = empName;
this.empNum = empNum;
this.empJob = empJob;
}
public int getEmpName()
{
return empName;
}
public String getEmpNum()
{
return empNum;
}
public String getEmpJob()
{
return empJob;
}
}
So, I tried this in main, using String as the key, so I want to use the names as the key so you can search by name. I'm also trying to manually fill it so I can test everything. Also, am I able to access say, the employee number on it's own? if so, how can I do this?
public class Main {
public static void main(String[] args)
{
Hashtable<String,Employee> EmployeeTable = new Hashtable<String,Employee>();
Employee Object = new Employee("Donald","Donald", 3, "Engineer");
}
}
Thanks in advance everyone!

You can add elements to your Hashtable using the put method. You just need to specify the key and the value.
Then you can retrieve values using the get method and specifying the key.
Example Usage:
Hashtable<String, Employee> table = new Hashtable<String, Employee>();
Employee bob = new Employee(...);
table.put("Bob", bob);
Then later you can say...
table.get("Bob");
and this will return Bob's Employee object for you.
Problems with your code:
There are a few problems with your code that you should be aware of.
1. Your Employee Constructor is wrong.
You've got a constructor for Product inside of your Employee class. This is illegal syntax and will not compile (I hope). Instead, you should use the Employee constructor.
2. Your Hashtable variable name matches the Object class.
You've named a variable Object. Object is the class that all java classes inherit from, so you really shouldn't name something this (if it even lets you at all).
The Object documentation mentions this...
Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.
3. Incorrect Hashtable types.
You've put the wrong types in your Hashtable declaration.
You wrote...
Hashtable<String, Employee> EmployeeTable = new Hashtable<String, Product>();
When really it should be...
Hashtable<String, Employee> employeeTable = new Hashtable<String, Employee>();
(Product changed to Employee)
(I also changed the variable to be lowercase)
Notes:
All of the documentation for Hashtable can be found here.
You may also be interested in using a HashMap instead of a Hashtable. They're almost identical but HashMap isn't threadsafe. You can see some of the differences here. If you really need a threadsafe map then I'd recommend ConcurrentHashMap, it's up to you to decide which one suits you the best though.
It's Java convention for variable names to start with lowercase letters. You don't have to follow this but it's definitely a good idea to. Syntax highlighters will no longer argue with you if you do.

What you want to achieve is rather this:
// Create my Hashtable using the diamond notation indicating to use the type arguments
// corresponding to the context which is <String, Employee> here
Map<String, Employee> EmployeeTable = new Hashtable<>();
Employee employee = new Employee("Donald", 3, "Engineer");
// put my employee into my map using empoyee's name as key
EmployeeTable.put(employee.getEmpName(), employee);
What you are looking for is Map#put(key, value)
After fixing several typo issues, your class Employee should be:
public class Employee {
...
public Employee(String empName, int empNum, String empJob)
{
...
}
public String getEmpName()
{
return empName;
}
public int getEmpNum()
{
return empNum;
}
...
}
NB: Hashtable is an outdated class, you should not use it anymore, if you don't intend to share it use an HashMap instead and if you want to share it use a ConcurrentHashMap

There are various things wrong with your class.
Example: the class is called Employee. Then the constructor must use that name, and nothing else!
So, it shouldn't read
public Product(String empName, int empNum, String empJob)
but
public Employee(String empName, int empNum, String empJob)
And then your call
Hashtable<String,Employee> EmployeeTable = new Hashtable<String,Product>();
could be correctly written down as
Hashtable<String,Employee> table = new Hashtable<>();
And no, a Hashtable is not a struct. A hashtable is a collection class; in other words: it is a Map. It maps a key (String in your case) to Employee objects.
But, well, stackoverflow is not a service where other people debug and explain your code to you. So, take my input as starting point; and for example: start reading the compiler messages.

Related

How can I make a java table where all rows can be used as keys?

This is hard for me to explain as I'm not native to the English language, so I will try setting up an example.
I am trying to save some data about a player in a class called PlayerData. It has three variables with getters and setters.
public class PlayerData {
private String player;
private String username;
private UUID uuid;
public String getPlayer() {
return player;
}
public void setPlayer(String player) {
this.player = player;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
}
For each player in the game, there will be generated a PlayerData object. Normally I would store this in a Map, so I can get the data about a player from eg. the UUID. However, I could use a way to be able to use any variable in the PlayerData object as "key", so I don't require the UUID to get the PlayerData. A way to do this (and my usual approach) would be to have multiple maps, something like this.
Map<String, PlayerData> playerMap;
Map<String, PlayerData> usernameMap;
Map<UUID, PlayerData> uuidMap;
The problem is, when it scales up with multiple variables, this gets annoying, and perhaps even eats up the RAM? I'm not entirely sure, as it stores references.
It similar to SQL, where you can also get specific colums based on the content of the rows. That's what I'm looking for, but without the SQL database.
I made a table explanation below in an attempt to explain it further:
Player
Username
UUID
Peter
Peter1234
657f6c48-655f-11eb-ae93-0242ac130002
Stephen
DogLover69
657f6efa-655f-11eb-ae93-0242ac130002
Joshua
XxFlowerPotxX
657f6fea-655f-11eb-ae93-0242ac130002
Short edition
I'm looking for a way to store multiple objects of the same type, where I (unlike Maps, that only take a single object as Key) can use multiple assigned variables as keys.
I hope the explaination was clear, I have absoloutly no idea how to explain it, which is probably also why I can't solve it by googling.
Thank you for your time.
As far as I understand, it's need to store various data for a specific user (and not just to update old values)
One way is through a custom map. Since only need a key (unique), could assume that username is doing that (eg:login). MyData can be customized further with what ever wanted to store.
Each key/username will contain a distinct list where new data is added.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class TestPData {
public static void main(String[] args)
{
TestPData t = new TestPData();
MyMap m = t.new MyMap();
//key can be just user name, if unique is assured
m.putMyData("player_1", t.new MyData("p1_data1"));
m.putMyData("player_1", t.new MyData("p1_data2"));
m.putMyData("player_2", t.new MyData("p2_data3"));
m.putMyData("player_3", t.new MyData("p2_data4"));
m.putMyData("player_3", t.new MyData("p2_data5"));
m.putMyData("player_3", t.new MyData("p2_data6"));
m.forEach((k,v)->{for(MyData d: v) {System.out.println(k+":"+d);}});
}
class MyData
{
String s;
public MyData(String s)
{
this.s = s;
}
public String toString()
{
return s;
}
}
class MyMap extends HashMap<String, List<MyData>>
{
private static final long serialVersionUID = 1L;
public void putMyData(String k, MyData d)
{
if(!this.containsKey(k))
{
this.put(k, new ArrayList<MyData>());
this.get(k).add(d);
}
else
{
this.get(k).add(d);
}
}
}
}
Output
player_1:p1_data1
player_1:p1_data2
player_3:p2_data4
player_3:p2_data5
player_3:p2_data6
player_2:p2_data3
If you are dealing with few records (some thousands), you can use a list and iterative search as suggested by #gilbert-le-blanc, but if you need to manage huge amounts of records/attributes, it is better to use a database anyway. You can also use an in-memory database like Derby or H2.
https://www.h2database.com/
https://db.apache.org/derby/
With some effort you can create a custom collection with multi-indexed properties also, but it is not worth the pain.
I would use a map of maps, with the first mapping by the name of the property and the second map by its value.
In code:
Map<String, Map<String, PlayerData>> index = new HashMap<>();
To add a mapping:
PlayerData peterData = new PlayerData(
"Peter",
"Peter1234",
"657f6c48-655f-11eb-ae93-0242ac130002");
index.computeIfAbsent("player", k -> new HashMap<>())
.put("Peter", peterData);
index.computeIfAbsent("username", k -> new HashMap<>())
.put("Peter1234", peterData);
index.computeIfAbsent("uuid", k -> new HashMap<>())
.put("657f6c48-655f-11eb-ae93-0242ac130002", peterData);
This navigates to the different inner maps (one per indexed property) by means of the Map.computeIfAbsent method, which creates an empty inner map and puts it into the outer map if it doesn't exist, or returns it if already present. Then, we add the mapping to the inner map by using Map.put as usual.
To remove a mapping:
index.computeIfAbsent("username", k -> new HashMap<>()).remove("Peter1234");
This is completely dynamic, as you don't have to change the data structure when you need to map by more properties. Instead, all you have to do is add mappings as needed.
The downside of this approach is that you'd need to use strings for the keys of the inner maps, but I think this is a reasonable trade-off.

How to create list of Maps from List of Object in java without having getKey method?

How to create a list of maps, where each key name is inferred from name of the class attribute, and value is to be put by getter method
I am having following class in java
class DTA {
private String id;
private String age;
#Override
public String toString() {
return "DTA{" +
"id='" + id + '\'' +
", age='" + age + '\'' +
'}';
}
public DTA(String id, String age) {
this.id = id;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
I am having a list of objects of type DTA
List<DTA> listA = new ArrayList<>();
listA.add(new DTA("A", "15"));
listA.add(new DTA("B", "25"));
I want to create an ordered list of maps (somewhat like scala) which has following content.
List<? extends Map<String, String>>
List(Map("id"->"A", "age"->"15"), Map("id"->"B", "age"->"25"))
Without "dynamics", the straight forward thing might look like:
List<Map<String, String>> x = listA
.stream()
.map(this::toMap)
.collect(Collectors.toList());
with a local helper, such as:
private Map<String, String> toMap(DTA dta) {
Map<String, String> rv = new HashMap<>();
rv.put("id", dta.getId());
rv.put("age", dta.getAge());
return rv;
}
In order to be fully dynamic here, you would have to use reflection to query the field names. You can find examples how to do that here.
But I strongly suggest to not do that: reflection should always be your last resort. The notion of DTA suggests that you have that object data coming from some "service" anyway. If so, why first serialize into a specific DTA class, to then "flatten" that information into some generic Map structure?!
Meaning: when that service gives you objects that are serialized as, say JSON, or XML ... then it would be much better to simply use a library like gson or jackson to directly deserialize that data into such generic "flat" Map-based objects. Jackson for example has a JsonNode class. When you deserialize into such objects, you get that mapping of field names for free! See here more example code.
The point is: identifying fields using reflection is possible. But reflection code is always tedious, and error prone. If possible, stay away from doing that yourself.
Basically, the tool used to "look into" the contents of classes in Java is called reflection. For example, if your object is a POJO (Plain Old Java Object), you can iterate over all fields in the class in the following way:
DTA obj; // some object, I assume it's initialized
Field[] fields = DTA.class.getDeclaredFields();
Map<String, Object> valuesMap = new HashMap<>();
for (field : fields) {
boolean wasAccessible = field.isAccessible(); // check if class has access control disabled
field.setAccessible(true); // disable access control (private/protected) to extract value
valuesMap.put(field.getName(), field.get(obj));
field.setAccessible(wasAccessible); // return flag to initial value
}
However, accessing values via reflection this way is notoriously hacky. Unless you have good reasons to do it yourself, try using a framework that automates tasks like that rather than writing code like this from scratch.
Also, reflection is slow. Accessing Field entities like that for every single object is suboptimal, if you ever want to really write code like this, you should cache the Field objects in a Map<String, Field> and only do the setAccessible override and the Field retrieval once for every collection of DTA objects.

Java: creating several instances of object in class itself or how to restructure

I'm a java beginner and have a question concerning how to best structure a cooking program.
I have a class called Ingredient, this class currently looks like this:
public class Ingredient {
private String identifier;
private double ingredientFactor;
private String titleInterface;
public Ingredient(String identifier, double ingredientFactor,String titleInterface) {
this.identifier = identifier;
this.ingredientFactor = ingredientFactor;
this.titleInterface = titleInterface;
}
I want to initialize several objects (about 40) with certain values as instance variables and save them in a Map, for example
Map<String, Ingredient> allIngredients = new HashMap<String, Ingredient>();
allIngredients.put("Almonds (ground)", new Ingredient("Almonds (ground)", 0.7185, "Almonds (ground)");
Later on I want to retrieve all these objects in the form of a Map/HashMap in a different class.
I'm not sure how to proceed best, initialize all these objects in the Ingredient class itself or provide a method that initializes it or would it be better to create an super class (AllIngredients or something like that?) that has a Map with Ingredients as instance variables?
Happy for any suggestions, thanks in advance :)
Please do not initialize all these objects in the Ingredient class itself. That would be a bad practice for oops.
Just think your class is a template from which you create copies(objects) with different values for attributes. In real world if your class represent model for a toy plane which you would use to create multiple toy planes but each bearing different name and color then think how such a system would be designed. You will have a model(class). Then a system(another class) for getting required color and name from different selection of colors and names present(like in database,files,property file ) etc.
Regarding your situation .
If predetermined values store the values in a text file,properties file,database,constants in class etc depending on the sensitivity of the data.
Create Ingredient class with constructors
Create a class which will have methods to initialize Ingredient class using predetermined values,update the values if required,save the values to text file -database etc and in your case return as map .
Also check the links below
http://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm
http://www.oracle.com/technetwork/java/dataaccessobject-138824.html
Sounds to me like you are looking for a static Map.
public class Ingredient {
private String identifier;
private double ingredientFactor;
private String titleInterface;
public Ingredient(String identifier, double ingredientFactor, String titleInterface) {
this.identifier = identifier;
this.ingredientFactor = ingredientFactor;
this.titleInterface = titleInterface;
}
static Map<String, Ingredient> allIngredients = new HashMap<String, Ingredient>();
static {
// Build my main set.
allIngredients.put("Almonds (ground)", new Ingredient("Almonds (ground)", 0.7185, "Almonds (ground)"));
}
}

Dynamic variable names in Java so each new object has separate name

How can I do such a thing?
String N = jTextField0.getText();
MyClass (N) = new Myclass();
Is it even possibe?
Or as my question's explains, how can I just make a method to create a new object of my specified class just with a different name each time I call it.
I really searched everywhere with no luck.
Thanks in Advance
P.S.
I wish you guys can excuse me for not being clear enough, Just to say it as it is, I made a textfield to get the name of someone who wants to make an account, and I made a class named "Customer". and a button named "Add". Now I want every time "Add" is clicked, compiler take what is in my textfield and make an object of the class "Customer" named with what it took from the textfield
It was too hard to read it in comments so I updated my question again, so sorry.
I'm stuck so bad. I suppose my problem is that I didn't "understand" what you did and only tried to copy it. This is what I wrote:
private void AddB0MouseClicked(java.awt.event.MouseEvent evt) {
String name = NameT0.getText();
Customer instance = new Customer(Name);
Customer.customers.add(instance);
and this is my Customer class:
public class Customer{
String name;
public Customer(String name){
this.name = name;
}
public String getName(){
return name;
}
static ArrayList<Customer> customers = new ArrayList<Customer>();
Variable names must be determined at compile time, they are not even part of the generated code. So there is no way to do that.
If you want to be able to give your objects names, you can use
Map<String, MyClass> map = new HashMap<>();
Add objects to the map like this (e.g):
map.put(userInput, new MyClass());
and retrieve objects like this:
MyClass mc = map.get(userInput);
I'm not entirely sure what you mean by...
how can I just make a method to create a new object of my specified
class just with a different name each time I call it
...but if I'm interpreting you correctly, I believe what you're trying to do as make MyClass accept a constructor parameter. You can do:
public class MyClass {
private String name;
public MyClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Then to create a new instance of MyClass, do:
String name = jTextField0.getText();
MyClass instance = new MyClass(name);
instance.getName(); // returns the name it was given
EDIT
Since you've added clarifications in the comments since I first answered this question, I thought I would update the answer to portray more of the functionality that you're looking for.
To keep track of the MyClass instances, you can add them to an ArrayList. ArrayList objects can be instantiated as follows:
ArrayList<MyClass> customers = new ArrayList<MyClass>();
Then for each MyClass instance you wish to add, do the following:
customers.add(instance);
Note that the ArrayList should not be reinstantiated for each instance that you wish to add; you should only instantiate the ArrayList once.

Benefits of factoring a new class vs string values for elements in a Set

This is more of a design question with implications for code simplicity vs. performance.
Lets say you want to make sure a set of values for a given user id are the same between two systems. The example here is to check that a student id has the same number of course enrollments in System A and System B.
For this we create:
List<String> studentList = new ArrayList<String>();
Set<String> sysAEnrollments = new HashSet<String>();
Set<String> sysBEnrollments = new HashSet<String>();
private Map<String, String> badEnrollList = new HashMap<String, String>();
And fill them appropriately, given a list of student ids(studentList):
studentList = getCurrentStudentList();
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.containsAll(sysBEnrollments)){
badEnrollList.put(id, getBadEnrollmentsById(id, sysAEnrollments, sysBEnrollments));
}
}
Question: What should the method 'getBadEnrollmentsById' return?
Either a concatenated string with enough meaning so it can just be printed out.
Or have a new object, for example another collection with the list of course ids that could be used for further processing but harder to use for printed output.
Is it worth designing thoroughly all expected objects or replace some of them with concatenated strings for clarity and performance?
NOTES:
System A is preferred as the authoritative source
Output from getBadEnrollmentsById should have all courses and flag those missing in system B.
PROPOSED SOLUTION: (2012-SEP-14)
EDIT (2012-SEP-17): Updated the Course class to include hashCode and equals
As suggested by user351721 I continued modelling the remaining objects that match the expected results/requirements.
Slight changes made a big difference and allowed me to go over this design flaw and finish with the implementation.
The revised collections are:
List<String> studentList = new ArrayList<String>();
Enrollment sysAEnrollments;
Enrollment sysBEnrollments;
Map<String, List<String>> badEnrollList = new HashMap<String, List<String>>();
And we populate the Enrollments:
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.getCourses().containsAll(sysBEnrollments.getCourses())){
List<String> missingCourses = getProblemEnrollmentListById(id, sysAEnrollments, sysBEnrollments);
badEnrollList.put(id, missingCourses);
}
}
So for now the output can be printed from badEnrollList by getting at each ArrayList and printing the course names. A course name with a * will mean that it's missing in sysB.
The Enrollment class looks like this:
public class Enrollment {
private Set<Course> courses = new HashSet<Course>();
public void setCourses(Set<Course> courses){
this.courses = courses;
}
public Set<Course> getCourses(){
return this.courses;
}
}
And the Course class ended up like this:
public class Course {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
// Must override hashCode() and equals()
#Override
public boolean equals(Object o){
if (o == this)
return true;
if (!(o instanceof Course))
return false;
Course c = (Course) o;
return c.id.equals(this.id) && c.name.equals(this.name);
}
#Override
public int hashCode(){
// Magic numbers as shown on Joshua Bloch's book "Effective Java" 2nd Edition, p.48
int result = 17;
result = 31 * this.id.hashCode();
result = 31 * this.name.hashCode();
return result;
}
}
The changes might look subtle but the important clue is that Enrollments are not a collection of strings, Enrollments are a collection of Courses AND each Course has a name and a availability property. They don't seem to do much but by using them I am defining the objects that I'm working with and documenting how these classes can be reused in the future.
"Growing Object-Oriented Software, Guided by Tests" addresses this question: chapter 7, "Value Types". Worth reading. An excerpt:
The more code we write, the more we’re convinced that we should define types to represent value concepts in the domain, even if they don’t do much. It helps to create a consistent domain model that is more self-explanatory. If we create, for example, an Item type in a system, instead of just using String, we can f ind all the code that’s relevant for a change without having to chase through the method calls
concatenated strings
would mean you have to define a pattern and corresponding set of valid strings and implement validation and translation to entity classes. Providing an interface or class would make it easier to update your code in a year or so, not to mention other programmers that might work with your application. Why not store student, enrollment or course objects in badEnrollList? How do these objects look like and what do you want to do with them?
In general: Yes, designing thoroughly all expected objects is worth it.
I feel that a collection, such as List<String> would be a desirable return value. This allows you to more efficiently capture multiple discrepancies between the two sets, and process the missing courses in your second object more intuitively. Printing the list wouldn't be that hard, either - depending on how you wished to convey the information.
It's also worth mentioning that the .equals() method for Set is a cleaner and more intuitive way to ensure equivalence between two sets.
Instead of using all these sets and maps, I'd use Plain Old Java Objects (POJOs) that reflect the actual business objects in question. From what you've indicated, you have Students who have an id of some sort, and who are enrolled in classes on System A and on System B. I would build up a set of Student objects defined like so:
public class Student {
private String id;
private List<String> enrollmentsA;
private List<String> enrollmentsB;
// appropriate getters and setters
}
Depending on if you want to do anything else with Classes, it may even be preferable to create some form of EnrolledClass object to represent that too.
Within the students class, I'd then have a method that would determine the "bad" enrollments. If all that you want to do with this data is generate an email message, it may even be as simple as a String:
public String getBadEnrollmentsMessage() {
List<String> enrolledBoth = getCommonEnrollments();
List<String> enrolledOnlyA = getAOnlyEnrollments();
List<String> enrolledOnlyB = getBOnlyEnrollments();
StringBuilder output;
// format the contents of the above lists into output
// format should be however you want it in the email.
return output.toString();
}
Then you could have a map of Students to email enrollments messages:
HashMap<Student, String> studentEmails;
for (Student s : allStudents) {
studentEmails.put(s, s.getBadEnrollmentsMessage());
}
Of course, if you have a method like getBadEnrollmentsMessage(), I'm not even sure you need the Map of students and strings in the first place. Frankly you could just create a sendEnrollmentEmail method, pass in a Student, and extract the message via getBadEnrollmentsMessage() right there.

Categories

Resources