How do you write a counter method to save space and time? - java

I am working with interface Multiset which is then used by two different classes: ArrayListMultiset and CounterMultiset
The ArrayListMultiset simply uses the .add method to put something in the list. So in a loop like,
Multiset<String> set = new Multiset<String>();
for(int i = 0; i < 10000; i++)
{
set.add("Hello");
}
this will cause the program to add Hello to a list 10,000 times.
Next we have CounterMultiset. It stores a Pair object (another class that takes in (T, Integer), where T is the String, "Hello" and Integer is the number of times it is trying to be added. I have written it like so:
public void add(Multiset<T> item)
{
if(!contains(item))
{
Pair newpair = new Pair(item, 0);
pairs.add(newpair);
}
for(int i = 0; i < pairs.size(); i++)
{
if(pairs.get(i).getFirst() == item)
{
pairs.get(i).changeSecond();
}
}
}
changeSecond() increments the second number in the Object by 1 to show that the word Hello has tried to be added again.
My question is, is this an appropriate way to save space and time for a program? When would it be faster to use a Counter and when would it be faster to simply add "Hello" 10,000 times?

Hello is an intern string in your code.
You will not have a copy of Hello for each element of ArrayListMultiset. You will have a reference to String Pool object.
What is faster for get/put (I assume) - depends on underlying data structures.

Related

Is there a way to loop execute a method while passing a different instance of a class to it each time?

My current code looks something like this:
public void myMethod()
{
instance1.myPanel.setVisible();
instance2.myPanel.setVisible();
instance3.myPanel.setVisible();
instance4.myPanel.setVisible();
//A bunch more
instance57.myPanel.setVisible();
}
Is there a ways to shorten it?
The Code below obviously doesn't work but gives you an idea of what I'm trying to do:
public void myMethod2(myClass instance1)
{
instance1.myPanel.setVisible();
}
int i = 1;
while(i <= 57)
{
myMethod2("instance" + i);
i++
}
In practice, this kind of problem is normally handled using some sort of collection, and its use will often fit naturally into the initialization of a program with a large number of objects. Rather than hand-writing the creation of 57 similar objects, one line at a time, you would create them in a loop, adding them to a collection as you do so.
A List implementation like ArrayList would be a good choice here, or one could simply use an array.
With a List:
/* During initialization of your program somewhere. */
List<MyClass> instances = new ArrayList<>();
for (int i = 0; i < 57; ++i) {
instances.add(new MyClass());
}
...
/* Later when you need to invoke a method: */
instances.forEach(instance -> instance.myPanel.setVisible());
You can build a list of your vars and just go through them:
List<ClassType> list = Arrays.asList( obj1, ob2, obj3 );
list.forEach( instance -> instance.myPanel.setVisible() );

Increment each object's value by 11 inside a list

I have a list of objects where list name is employments and it has 'n' number of objects called employments.Each employment object has variable called serialnumber. Now i need to increment serialnumber for each object by 11.
Here is code
for(Employment employment:employments.getEmployemnts()){
if(employment="GENERAL_MANAGER"){
employement.setSerialNumberForGenManager()
}else{
employment.setSerialNumberForOthers()
}
Inside the employment class:
public static employemntIndex=11;
public employemnt setSerialNumberForGenManager(){
this.serialNumber = 0;
}
public employemnt setSerialNumberForOthers(){
this.serialNumber = employemntIndex+serialNumber;
}
Now,i'm not able to increment values by 11. The result for every object is always 11 only.It is not getting incremented.
This may be because of your String comparison. String comparisons should use the Object#equals(Object) method (as opposed to ==);
if (yourString.equals("COMPARISON STRING")) //do stuff
This is because String is an instance (which happens to contain text), and the the == operator in this case only tests the references, not whether the instances themselves contain the same text.
Also, = is an assignment operator, == should be used for most comparisons (other than 'special cases', such as comparing Strings).
The next problem is that you are not carrying the values over (as you say). To fix this;
public static employemntIndex = 11;
//Increase the employment index for each GENERAL_MANAGER
public employemnt setSerialNumberForOthers(){
this.serialNumber = employmentIndex;
employmentIndex += 11;
}
Try like this
public static Integer employemntIndex = 0;
public employemnt setSerialNumberForOthers(){
this.serialNumber = employemntIndex;
employemntIndex += 11;
}
Two possible errors:
You are using a so called "enhanced for loop".
You can't edit the elements of a collection that way as that loop is read-only.
To edit elements you have to use a for loop with an explicit iteration index or an iterator.
You seem to be comparing a reference (memory address with a String that is unlikely to correspond to a memory address.
employment="GENERAL_MANAGER"
You probably forgot to call a getter method like
employment.getRole().equals("GENERAL_MANAGER")
So it would be (if size() method is not supported you should use array.length)
for(int i = 0; i < employments.getEmployemnts().size(); i++) {
if(employments[i].yourGetter().equals("GENERAL_MANAGER")){
employements[i].setSerialNumberForGenManager()
}else{
employments[i].setSerialNumberForOthers()
}
Or if your collection implements the List interface
Iterator iter = employments.getEmployments().iterator();
while (iter.hasNext()){
Employment employment = iter.next();
if(employment.yourGetter().equals("GENERAL_MANAGER")){
employement.setSerialNumberForGenManager()
}else{
employment.setSerialNumberForOthers()
}
}
Hope that helped
Edit:
as KookieMonster has noticed you are using the assignment operator within the IF condition you should use the equals method

I get a compile time error when accessing an ArrayList with a new method

I am attempting to access an ArrayList that was created in a different method within the same class. The scanner method pulls in data from a text file. The text file data appears this way: 123 12 1 43, with line breaks...
Currently, my method works to pull in the data, but does not compile after that method ends. I originally had the entire code within the same method and it worked fine. But I'd like to return the largest value by creating a new method within this class, and then create a tester class that will access this new method. Here is my existing code. Or if there is a better solution. I'm all ears.
public class DataAnalyzer {
public DataAnalyzer(File data) throws FileNotFoundException
{
List<Integer> rawFileData = new ArrayList<>();
FileReader file = new FileReader("info.txt");
try (Scanner in = new Scanner(file)) {
while(in.hasNext())
{
rawFileData.add(in.nextInt());
}
}
}
public int getLargest(rawFileData){
int largest = rawFileData.get(0);
for (int i = 1; i < rawFileData.size(); i++){
if (rawFileData.get(i) > largest)
{
largest = rawFileData.get(i);
}
}
for (Integer element : rawFileData){
if (element == largest)
{
System.out.print("This is the Largest Value: ");
System.out.print(element);
}
}
}
}
Your main issue is with your method declaration. It needs a type parameter:
public int getLargest(List<Integer> rawFileData)
Note the List<Integer>.
Now, there is already a method for this in the Collections utility class. You would do well to look over that link in detail - there are many useful methods there. To get the highest element from a Collection of Objects that have a natural order (such a Integer). For example
int largest = Collections.max(rawFileData)
So your method can be reduced to:
public int getLargest(List<Integer> rawFileData)
return Collections.max(rawFileData);
}
You need to think over your logic much more carefully before you begin to write code, for example, your first loop is good:
int largest = rawFileData.get(0);
for (int i = 1; i < rawFileData.size(); i++){
if (rawFileData.get(i) > largest)
{
largest = rawFileData.get(i);
}
}
You do exactly what any programmer would do. But then, instead of returning the largest when you find it, you for some reason loop again:
for (Integer element : rawFileData){
if (element == largest)
{
System.out.print("This is the Largest Value: ");
System.out.print(element);
}
}
Ask yourself what does this do? You have a List of, say, apples. You look at each one and compare them - finding the largest apple. You now have the largest apple in the List. You then loop over the List again looking for an apple that matches the apple you have already found. Why do this?
Further, you never return from the method. Your method is declared as returning an int; but you never do.
The missing type in your method definition is the problem here.
Change the method definition from
public int getLargest(rawFileData) {
....
}
to
public void getLargest(List<Integer> rawFileData) {
....
}
And the second for loop in the method is unnecessary. The largest integer is already stored in the variable "largest" and you can print it after the first for loop.

Exponentially increasing amounts of time to repeat a function

I've written my own math parser and for some reason it takes increasing amounts of time to parse when I tried to profile the parser.
For testing I used this input: Cmd.NUM_9,Cmd.NUM_0,Cmd.NUM_0,Cmd.DIV,Cmd.NUM_2,Cmd.ADD,Cmd.NUM_6,Cmd.MULT,Cmd.NUM_3
Single execution ~1.7ms
3000 repeats ~ 1,360ms
6000 repeats ~ 5,290ms
9000 repeats ~11,800ms
The profiler says 64% of the time was spent on this function:
this is my function to allow implicit multiplications.
private void enableImplicitMultiplication(ArrayList<Cmd> input) {
int input_size = input.size();
if (input_size<2) return;
for (int i=0; i<input_size; i++) {
Cmd cmd = input.get(i);
if (i>0) {
Cmd last = input.get(i-1);
// [EXPR1, EXPR2] => [EXPR1, MULT, EXPR2]
boolean criteria1 = Cmd.exprnCmds.contains(cmd) && Cmd.exprnCmds.contains(last);
// [CBRAC, OBRAC] => [CBRAC, MULT, OBRAC]
// [NUM_X, OBRAC] => [NUM_X, MULT, OBRAC]
boolean criteria2 = cmd==Cmd.OBRAC && (last==Cmd.CBRAC || Cmd.constantCmds.contains(last));
// [CBRAC, NUM_X] => [CBRAC, MULT, NUM_X]
boolean criteria3 = last==Cmd.CBRAC && Cmd.constantCmds.contains(cmd);
if (criteria1 || criteria2 || criteria3) {
input.add(i++, Cmd.MULT);
}
}
}
}
What's going on here??
I executed the repeats like this:
public static void main(String[] args) {
Cmd[] iArray = {
Cmd.NUM_9,Cmd.NUM_0,Cmd.NUM_0,Cmd.DIV,Cmd.NUM_2,Cmd.ADD,Cmd.NUM_6,Cmd.MULT,Cmd.NUM_3
};
ArrayList<Cmd> inputArray = new ArrayList<Cmd>(Arrays.asList(iArray));
DirtyExpressionParser parser = null;
int repeat=9000;
double starttime = System.nanoTime();
for (int i=0; i<repeat; i++) {
parser = new DirtyExpressionParser(inputArray);
}
double endtime = System.nanoTime();
System.out.printf("Duration: %.2f ms%n",(endtime-starttime)/1000000);
System.out.println(parser.getResult());
}
Constructor looks like this:
public DirtyExpressionParser(ArrayList<Cmd> inputArray) {
enableImplicitMultiplication(inputArray); //executed once for each repeat
splitOnBrackets(inputArray); //resolves inputArray into Expr objects for each bracket-group
for (Expr expr:exprArray) {
mergeAndSolve(expr);
}
}
Your microbenchmark code is altogether wrong: microbenchmarking on the JVM is a craft in its own right and is best left to dedicated tools such as jmh or Google Caliper. You don't warm up the code, don't control for GC pauses, and so on.
One detail which does come out by analyzing your code is this:
you reuse the same ArrayList for all repetitions of the function call;
each function call may insert an element to the list;
insertion is a heavyweight operation on ArrayList: the whole contents of the list after the inserted element must be copied.
You should at least create a fresh ArrayList for each invocation, but that will not make your whole methodology correct.
From our discussion in the comments I diagnose the following issue you may have with understanding your code:
In Java there is no such thing as a variable whose value is an object. The value of the variable is a reference to the object. Therefore when you say new DirtyExpressionParser(inputArray), the constructor does not receive its own private copy of the list, but rather a reference to the one and only ArrayList you have instantiated in your main method. The next constructor call gets this same list, but now modified by the earlier invocation. This is why your list is growing all the time.

Why do I get a "duplicate local variable" error?

I have a loop in which I calculate a value and add it it a list. So, I do something like that:
x = getValue()
values.add(x)
while (true) {
x = getValue();
values.add(x)
}
I found out that this approach does not work since I add the same instance to the list. In more details, in every cycle of the loop I re-assign a new value to the x and doing so I change values of all elements that were already added to the list (so in the end I get a list of identical elements).
To solve this problem I did the following:
x = getValue();
Integer[] valueToAdd = new Integer[n];
for (int i=0; i<n; i++) {
valueToAdd[i] = x[i];
}
while (true) {
x = getValue();
y = new Integer[n];
for (int i=0; i<n; i++) {
valueToAdd[i] = x[i];
}
values.add(valueToAdd)
}
In this way I wanted to create a new instance every time want to add a value to the list. But it does not work since I get a duplicate local variable error.
It is also strange to me that I do not have this error if I declare the same variable many times in the loop. The problem appears only if I first declare a new variable outside the loop and then also in the loop.
Is there a way in Java to re-use the same name for different instances?
ADDED
I need to clarify some issues. I did not show all the code. I have the break command in the loop (when a new value cannot be generate, I exit the loop). x and value have Integer[] type.
ADDED 2
Since it was mentioned that the problem can be in the getValue() I need to in more details here. Actually I do not have getValue() in my code (I used getValue() here to make my example shorter). In my code I had:
Integer[] x = new x[n];
while (true) {
for (int i=0; i<n; i++) {
x[i] = y[i];
}
values.add(x)
}
And it did not work since in my values list I had identical elements (and I know that in the loop on every cycle x had a new value).
ADDED 3
Why all elements of my list seems to be the same?
Your problem is not what you think it is. For example take a look at this simple program:
String x = null;
List<String> l = new ArrayList<String>();
for (int i = 0; i < 10; i ++) {
x = String.valueOf(i);
l.add(x);
}
System.out.println(l);
It prints the numbers from 0 to 9. This is because java is pass-by-value (check here). You are not passing the reference to x, you are passing the value of x (in the add method).
So the problem lies in the getValue() method, which returns the same object.
Update: Now the question makes more sense. You are working with the same object x everytime, and just changing its state. In order to put different values just move the declaration inside the loop:
while (true) {
Integer[] x = new x[n];
...
}
If you need it outside the loop, well, simply use another variable there. It does not have to be named x. Since you won't be using it inside the loop anyway.

Categories