how to get only one key in a HashMap with "similar" keys - java

I have an object "ObjectName" defined by String hostName and List serviceList.
two serviceLists might contain one or more of the same string.
Each string is the name of a method. There can't be methods with same name that do different things or methods with different names that do the same things.
Each ObjectName is paired with an unique integer. Then I create a
Map<ObjectName, Integer> objectPorts = new HashMap<>();
I add to this map two nodes with a duplicate string in their serviceList
example:
objectName1's serviceList has {method1, method2, method3}, while objectName2's serviceList has {method4, method2, method5}
objectPorts contains two times the string "method2"
Now I want to search the map for "method2" and execute the code of method2. I want the execution of the code to be done only once, not for each entry of "method2".
how to do this?
I was using this code: (edit: added the switch portion for clarification)
public long executeMethod(String methodName, int n1, int n2) throws Exception {
long result = 0;
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) {
switch(methodName) {
case "method1":
result = arithmeticClient.method1(n1, n2);
break;
case "method2":
result = arithmeticClient.method2(n1, n2);
break;
case "method3":
result = arithmeticClient.method3(n1, n2);
break;
case "method4"
result = arithmeticClient.method4(n1, n2);
break;
}
}
return result;
}
but it executes the code of methodName for each instance of methodName present in the map, so it's not what i want it to do. how can I modify it?

From what I am understanding, all you would need to do is break out of the for loop once it has executed. So something like this --
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) {
does something
break;
}
}
break is actually a java keyword that "breaks" out of the current loop you are in. So adding it after the code "does something" would mean the loop would exit and the code would only be executed the one time.

The simple way to fix this would be just to add a boolean to indicate if method 2 had been called previously during the loop:
public long executeMethod(String methodName, int n1, int n2) throws Exception {
long result 0;
boolean executed = false;
for(Map.Entry<Object, Integer> pair : something.entrySet()) {
List<String> methods = pair.getkey().serviceList;
if(methods.contains(methodName) && !executed){
//do something
executed = true;
}
}
return result;
}
This will stop method 2 or whatever from being executed more than one time if the value is contained in multiple Lists associated with the objects of your map.

Related

Changing the name of the method called dynamically in java

I have an object "JudgesSubmission" with the following methods:
public String getInnovationGrade1() {
return innovationGrade1;
}
public String getInnovationGrade2() {
return innovationGrade2;
}
public String getInnovationGrade3() {
return innovationGrade3;
}
public String getInnovationGrade4() {
return innovationGrade4;
}
Now, when calling these methods, I want to put them in a loop where the called method name gets the index of the loop attached to its end changing the method called. Is this possible?
For example, the following code would never work, but I am writing it to explain what I need:
judgesSubmission metricScores= new judgesSubmission;
int metricSum=0;
for (int i=0;i<4;i++){
metricSum=metricSum
Integer.parseInt(metricScores.getInnovationGrade+"i"());
}
Is there a way to do that or do I always have the full method name written?
What you want to do is not possible... but with reflection such as :
MyObject.class.getMethod("mymethod"+i);
Without reflection you could use a Supplier<String> :
public void process(Supplier<String>... suppliers){
judgesSubmission metricScores= new judgesSubmission;
int metricSum=0;
for (Supplier<String> supplier : suppliers){
Integer.parseInt(supplier.get());
}
}
And call it such as :
MyObject myObject = new MyObject();
process(()->myObject.getInnovationGrade1(),
()->myObject.getInnovationGrade2(),
()->myObject.getInnovationGrade3(),
()->myObject.getInnovationGrade4());
It is not possible without reflection (and is highly not recommended)
Instead you may want to use other methods:
An array of the data (either replacing the 4 methods, or in addition)
String[] getInnovationGrades()
{
return new String[]{innovationGrade1, innovationGrade2, innovationGrade3, innovationGrade4};
}
Then later you can use
for(String innovationGrade : getInnovationGrades())
//do stuff
An argument to get the data you want
String getInnovationGrade(int i)
{
switch(i)
{
case 1:
return getInnovationGrade1();
case 2:
return getInnovationGrade2();
case 3:
return getInnovationGrade3();
case 4:
return getInnovationGrade4();
default:
return ""; //or throw exception, depends on how you wish to handle errors
}
}
Then later you can use
for(int i = 1; i <= 4; i++)
getInnovationGrade(i); //and do stuff with it

How to replace multiple if-else statements to optimize code?

I want to know if there is any way i could optimize this code.
String[] array;
for(String s:array){
if(s.contains("one"))
//call first function
else if(s.contains("two"))
//call second function
...and so on
}
The string is basically lines I am reading from a file.So there can be many number of lines.And I have to look for specific keywords in those lines and call the corresponding function.
This wont stop you code from doing many String#contains calls, however, it will avoid the if/else chaining..
You can create a key-function map and then iterate over the entries of this map to find which method to call.
public void one() {...}
public void two() {...}
private final Map<String, Runnable> lookup = new HashMap<String, Runnable>() {{
put("one", this::one);
put("two", this::two);
}};
You can then iterate over the entry-set:
for(final String s : array) {
for(final Map.Entry<String, Runnable> entry : lookup) {
if (s.contains(entry.getKey())) {
entry.getValue().run();
break;
}
}
}
You can use switch, but in this case i think the if else is the best way
Since you stated that the order of the checks is not important, you can use a combination of regular expression matching and switch:
static final Pattern KEYWORDS=Pattern.compile("one|two|tree|etc");
 
Matcher m=KEYWORDS.matcher("");
for(String s:array) {
if(m.reset(s).find()) switch(m.group()) {
case "one": //call first function
break;
case "two": //call second function
break;
case "three": //call third function
break;
case "etc": // etc
break;
}
}
Since this will stop at the first match, regardless of which keyword, it is potentially more efficient than checking one keyword after another, for strings containing a match close to the beginning.

Java - Increase the value of an int size method by 1 everytime the method is called

I'm stuck on this one question I can't get my head around. I need to write a method to increase the number of "votes" of a specific "act" by one and then print out the updated vote count for that specific act. I'm working with ArrayLists here as well to point out.
Here is the logic you want to follow:
1: Iterate through ArrayList of 'acts'
2: Check for specified 'act'
3: If 'act' equals specified 'act', add one to your counter variable (votes++)
This is as much information as I'll give out without code to show what you've tried!
You could use a Map:
Class VoteCounter {
Map<Integer, Integer> actToCounterMap = new HashMap<Integer, Integer>();
public void raiseVoteForAct(int actId) {
if (actToCounterMap.contains(actId) {
int curVote = actToCounterMap.get(actId);
curVote++;
actToCounterMap.put(actId, curVote);
} else {
// init to 1
actToCounterMap.put(actId, 1);
}
}
}
You can print entire objects out in java, such as
System.out.println("Array list contains: " + arrayListName);
which will print the contents of the array without iterating through each value, although it may have odd syntax. As for the "acts", which I assume you mean objects, if you want to iterate the number of votes by one, you can have a class like this:
public class Act{
int votes = 0;
public void increaseVote(){
votes ++;
//You can also do votes = votes + 1, or votes += 1, but this is the fastest.
}
//While were at it, let's add a print method!
pubic void printValue(){
System.out.println("Votes for class " + this.getClass().getName() + " = " + votes + ".");
}
}
Finally, for a class with the arrayList:
class classWithTheArrayList {
private ArrayList<Act> list = new ArrayList<Act>();
public static void main(String[] args){
Act example1 = new Act();
list.add(example1);
//ArrayLists store a value but can't be changed
//when in the arraylist, so, after updating the value like this:
Act example2 = new Act();
example2.increaseVote();
//we need to replace the object with the updated one
replaceObject(example1, example2);
}
public void replaceObject(Object objToBeRemoved, Object objToReplaceWith){
list.add(objToReplaceWith, list.indexOf(objToBeRemoved); //Add object to the same position old object is at
list.remove(objToBeRemoved); //Remove old object
}
}
A slightly more efficient vote counter.
class VoteCounter<T> {
final Map<T, AtomicInteger> actToCounterMap = new HashMap<>();
public void raiseVoteForAct(T id) {
AtomicInteger ai = actToCounterMap.get(id);
if (ai == null)
actToCounterMap.put(id, ai = new AtmoicInteger());
ai.incrementAndGet();
}
}
Instead of AtomicInteger you can use new int[1] but it's relatively ugly. ;)

Iterating through hashmap and creating unique objects - trying to prevent duplicates

I explain what I am trying to do in comments above the parts in the method:
public int addPatron(String name) throws PatronException {
int i = 0;
//1. Iterate through a hashmap, and confirm the new name I am trying to add to the record doesn't already exist in the hashmap
for (Map.Entry<Integer, Patron> entry : patrons.entrySet()) {
Patron nameTest = entry.getValue();
//2. If the name I am trying to add already exists, we want to throw an exception saying as much.
if (nameTest.getName() == name) {
throw new PatronException ("This patron already exists");
//3. If the name is unique, we want to get the largest key value (customer number) already in the hash, an increment by one.
} else if (nameTest.getName() != name) {
Map.Entry<Integer,Patron> maxEntry = null;
for(Map.Entry<Integer, Patron> entryCheck : patrons.entrySet()) {
if (maxEntry == null || entryCheck.getKey() > maxEntry.getKey()) {
maxEntry = entryCheck;
i = maxEntry.getKey();
i++;
}
}
} else {
throw new PatronException("Something's not working!");
}
//4. If everything is ok up to this point, we want to us the name and the new customer id number, and use those to create a new Patron object, which then gets added to a hashmap for this class which contains all the patrons.
Patron newPatron = new Patron(name, i);
patrons.put(i, newPatron);
}
return i;
}
When I try and run a simple unit test that will fail if I successfully add the same name for addPatron twice in a row, the test fails.
try {
testLibrary.addPatron("Dude");
testLibrary.addPatron("Dude");
fail("This shouldn't have worked");
The test fails, telling me the addPatron method is able to use the same name twice.
#Jon Skeet:
My Patron class looks like this:
public class Patron {
//attributes
private String name = null;
private int cardNumber = 0;
//operations
public Patron (String name, int cardNumber){
this.name = name;
this.cardNumber = cardNumber;
}
public String getName(){
return name;
}
public int getCardNumber(){
return cardNumber;
}
}
As others have said, the use of == for comparing strings is almost certainly inappropriate. However, it shouldn't actually have caused a problem in your test case, as you're using the same constant string twice, so == should have worked. Of course, you should still fix the code to use equals.
It's also not clear what the Patron constructor or getName methods do - either of those could cause a problem (e.g. if they create a new copy of the string - that would cause your test to fail, but would also be unnecessary usually).
What's slightly more worrying to me is this comment:
// 3. If the name is unique, we want to get the largest key value (customer number)
// already in the hash, an increment by one.
This comment is within the main loop. So by that point we don't know that the name is unique - we only know that it doesn't match the name of the patron in this iteration.
Even more worrying - and I've only just noticed this - you perform the add within the iteration block too. It seems to me that you should have something more like this:
public int addPatron(String name) throws PatronException {
int maxKey = -1;
for (Map.Entry<Integer, Patron> entry : patrons.entrySet()) {
if (entry.getValue().getName().equals(name)) {
// TODO: Consider using IllegalArgumentException
throw new PatronException("This patron already exists");
}
maxKey = Math.max(maxKey, entry.getKey());
}
int newKey = maxKey + 1;
Patron newPatron = new Patron(name, newKey);
patrons.put(newKey, newPatron);
return newKey;
}
Additionally, it sounds like really you want a map from name to patron, possibly as well as the id to patron map.
You need to use equals to compare String objects in java, not ==. So replace:
if (nameTest.getName() == name) {
with:
if (nameTest.getName().equals(name)) {
Try to use
nameTest.getName().equals(name)
instead of
nameTest.getName() == name
because now you're comparing references and not the value of the String.
it's explained here
Took another look on your code
Well i took another look on your code and the problem is, that your HashMap is empty at the start of the Test. So the loop will never be runned ==> there will never bee a Patron added or an Exception thrown.
The cause of the problem is how you have used the compare operator ==.
When you use this operator against two objects, what you test is that variable point to the same reference.
To test two objects for value equality, you should use equals() method or compareTo if available.
For String class, invoke of equals is sufficient the check that the store same characters more.
What is equals method ?
To compare the values of Object
The problem is how you compare names.

New in Java (Map and Set)

Can Any help me and Check my answer
(a) Declare a private instance variable (Attribute) called HouseMap which should hold an unsorted map with integer keys and string values.
Private Map< Integer, String> HouseMap = new HashMap<Integer, String>();
(b) Write a zero-argument constructor of HouseCatalogue that initializes HouseMap to an empty map.
houseMap = new HashMap<Integer, String>();
(c) Write an instance method called addHouse() for the HouseCatalogue class that takes no arguments, and returns no value. This method should simply enter the four entries shown above into the HouseMap.
Public void addHouse()
{
HouseMap.put(101," adison Sas") ;
HouseMap.put(103," FourSeasons") ;
HouseMap.put(105," Hayat Regency ");
HouseMap.put(107," Concord al-Salam ") ;
}
(d) Write an instance method called printHouse() for the HouseCatalogue class that takes an integer argument, and return a string value. This method should print the value (House name) of the area code that is equal to integer argument and return it. Otherwise it will return null.
Public string printHouse( int area)
{
for(Integer eachcode : HouseMap.keySet())
{
if ( HouseMap.keySet()== area)
{
System.out.println("House name is"+ HouseMap.get(eachcode));
}
}
}
or
public static void printHouse( int area)
{
for(Map.Entry<Integer,String> entry : houseMap.entrySet())
{
if (entry.getKey().equals(area))
{
System.out.println("House name is"+ entry.getValue());
//return entry.getValue(); // return it
}
}
}
(a) Lower case letter for private and no new HashMap() needed when declaring. Normally when useing java convensions you use camelcase when declaring your variasbles (houseMap) but it's fine.
private Map<Integer, String> HouseMap;
(b) You have declared your variable with HouseMap not houseMap (see (a) camelcase) so initializing it needs the same variable:
HouseMap = new HashMap<Integer, String>();
(c) Seems fine
(d) Hum, don't see the point in the method, it should both print the value and return it.. well.. first off public lower case letters again, String with a big letter (name of the class` and then the implementation:
public String printHouse(int area) {
if (HouseMap.containsKey(area)) {
String name = HouseMap.get(area);
System.out.println("The house with the area code " + area +
" is " + name));
return name;
}
return null;
}
a) only declare the variable - do not initialize it
b) ok
c) ok
d) in a map you have random access. look at Map#get(Integer) API. you don't need to iterate over the entry set
Since the key of a map is unique, you can simplify the last method as follows:
public static void printHouse( int area)
{
String name = houseMap.get(area); // May return null
System.out.println("House name is " + name);
return name;
}
public and private must be written with a lowercase p everywhere.
You should show the entire constructor, not just the code that goes in it.
Fix your indentation. Use the same amount of indentation for every level, and make sure that everything lines up neatly.
When you use a foreach loop like for (Integer eachcode: HouseMap.keySet()), the iteration variable is eachcode. This is the value that you should compare to area, because that's what the integer is. You don't want to compare the supplied to all of the area codes taken as a single unit (those aren't the same kind of thing); you want to compare it to each area code in turn.
But you don't want to write that loop at all. The point of a HashMap is to let you look up the value, given the key. That is what .get() does. You have the key: it is area. So all you need to do is look it up: System.out.println("House name is " + HouseMap.get(area)).
You also need to return the name that you looked up, not just print it, and you need to check that the name is there (use .contains()) before printing.
It looks like somebody else commented your code to say "you also forgot to return it". Did you try talking to this person, or reading the comments?
Just a hint for the last one:
(d) Write an instance method called
An instance method is not a static method, you have to remove the static keyword in your second (d) method...
Thanks alot for every body
public static String printHouse(int
code) {
if (houseMap.containsKey(code))
{
String name = houseMap.get(coe);
System.out.println(code+ " : " + name);
return name;
} else{
System.out.println("null");
return null; }

Categories