putIfAbsent always returning null - java

I am working on a wordcount program, where I create N threads, and each thread receives a char[] buffer containing many different numbers like [2355 3326 94438 123 123...] I want to create a map where the key is the number itself, and the value is how many time it appears. I am converting from the char[] array to an integer as shown below.
However it seems that every time I call putIfAbsent(), it always returns null, meaning it did not find a key of that value. This does not make sense, as I have thousands of duplicate values in my text file. The map should end up being around 300kb, instead it is more than a gigabyte.
newbyte[] is a char[] containing ONLY numbers and spaces
Why does putIfAbsent always return null?
Also, when I print the map when finished, it looks like this:
233303192 = 1
1770057208 = 1
1323329638 = 1
1278321050 = 1
962422124 = 1
472527478 = 1
936125441 = 1
-350637153 = 1
-601349585 = 1
Which is quite odd, because the largest value of any input is 65535. Not sure how this makes any sense.
public void run() {
int counter = 0; int i; Integer check; int j =0; int temp = 0; int intbuilder = 0;
for (i = 0; i < newbyte.length; i++) {
if (newbyte[i] != ' ') { //delimiter is not found, so add to temp char array
intbuilder = (intbuilder * 10) + (int)newbyte[i];
counter++;
}
else {
check = wordCountMap.putIfAbsent(intbuilder, 1);
if (check != null) {
wordCountMap.put(intbuilder, check + 1);
}
intbuilder = 0;

The problem is not in the concurrent hash map. Its with the way you parse the character array
public void startThreads() throws InterruptedException{
char[] input = {'1',' ','2',' ','3','4',' ','1',' ','2',' ','3','4',' ','1'};
Thread workerThread = new Thread(new Worker(input));
workerThread.start();
workerThread.join();
System.out.println("Count for 1 & 2 are "+countMap.get(1)+" and "+countMap.get(2));
}
private class Worker implements Runnable{
private char[] newbyte;
public Worker(char[] newbyte){
this.newbyte = newbyte;
}
#Override
public void run() {
int number=0;
for(int i=0;i< newbyte.length;i++){
if(newbyte[i] != ' '){
number = (number*10)+Character.getNumericValue(newbyte[i]);
}else{
Integer currentValue = countMap.putIfAbsent(number, 1);
if(currentValue != null){
countMap.put(number, currentValue+1);
}
number = 0;
}
}
Integer currentValue = countMap.putIfAbsent(number, 1);
if(currentValue != null){
countMap.put(number, currentValue+1);
}
}
}

Related

Fixing a looping issue for removing letters in a String

So i'm making a program that removes duplicate letters in a string. The last step of it is updating the old string to the new string, and looping through the new string. I believe everything works besides the looping through the new string part. Any ideas what might be causing it to not work? It will work as intended for one pass through, and then after that it won't step through the new loop
public class homework20_5 {
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
int i;
for (i = 0; i < kb.length(); i++) {
char temp = kb.charAt(i);
if(temp == kb.charAt(i+1)) {
kb = kb.replace(""+temp, "");
i = kb.length() + i;
}
}
System.out.println(kb);
}
}
Instead of using complex algorithms and loops like this you can just use HashSet which will work just like a list but it won't allow any duplicate elements.
private static String removeDuplicateWords(String str) {
HashSet<Character> xChars = new LinkedHashSet<>();
for(char c: str.toCharArray()) {
xChars.add(c);
}
StringBuilder sb = new StringBuilder();
for (char c: xChars) {
sb.append(c);
}
return sb.toString();
}
So you actually want to remove all occurrences that appear more than once entirely and not just the duplicate appearances (while preserving one instance)?
"Yea that’s exactly right "
In that case your idea won't cut it because your duplicate letter detection can only detect continuous sequences of duplicates. A very simple way would be to use 2 sets in order to identify unique letters in one pass.
public class RemoveLettersSeenMultipleTimes {
public static void main(String []args){
String input = "abcabdgag";
Set<Character> lettersSeenOnce = lettersSeenOnceIn(input);
StringBuilder output = new StringBuilder();
for (Character c : lettersSeenOnce) {
output.append(c);
}
System.out.println(output);
}
private static Set<Character> lettersSeenOnceIn(String input) {
Set<Character> seenOnce = new LinkedHashSet<>();
Set<Character> seenMany = new HashSet<>();
for (Character c : input.toCharArray()) {
if (seenOnce.contains(c)) {
seenMany.add(c);
seenOnce.remove(c);
continue;
}
if (!seenMany.contains(c)) {
seenOnce.add(c);
}
}
return seenOnce;
}
}
There are a few problems here:
Problem 1
for (i = 0; i < kb.length(); i++) {
should be
for (i = 0; i < kb.length() - 1; i++) {
Because this
if (temp == kb.charAt(i+1))
will explode with an ArrayIndexOutOfBoundsException otherwise.
Problem 2
Delete this line:
i = kb.length() + i;
I don't understand what the intention is there, but nevertheless it must be deleted.
Problem 3
Rather than lots of code, there's a one-line solution:
String deduped = kb.replaceAll("[" + input.replaceAll("(.)(?=.*\\1)|.", "$1") + "]", "");
This works by:
finding all dupe chars via input.replaceAll("(.)(?=.*\\1)|.", "$1"), which in turn works by consuming every character, either capturing it as group 1 if it has a dupe or just consuming it if a non-dupe
building a regex character class from the dupes, which is used to delete them all (replace with a blank)
Say you feed the program with the input "AAABBC", then the expected output should be "ABC".
Now in the for-loop, i gets incremented from 0 to 5.
After 1st iteration:
kb becomes AABBC and i becomes 5 + 0 = 5 and gets incremented to 6.
And now the condition for the for-loop is that i < kb.length() which equates to 6 < 5 returning false. Hence the for-loop ends after just one iteration.
So the problematic line of code is i = kb.length() + i; and also the loop condition keeps changing as the size of kb changes.
I would suggest using a while loop like the following example if you don't worry too much about the efficiency.
public static void main(String[] arg) {
String kb = "XYYYXAC";
int i = 0;
while (i < kb.length()) {
char temp = kb.charAt(i);
for (int j = i + 1; j < kb.length(); j++) {
char dup = kb.charAt(j);
if (temp == dup) {
kb = removeCharByIndex(kb, j);
j--;
}
}
i++;
}
System.out.println(kb);
}
private static String removeCharByIndex(String str, int index) {
return new StringBuilder(str).deleteCharAt(index).toString();
}
Output: XYAC
EDIT: I misunderstood your requirements. So looking at the above comments, you want all the duplicates and the target character removed. So the above code can be changed like this.
public static void main(String[] arg) {
String kb = "XYYYXAC";
int i = 0;
while (i < kb.length()) {
char temp = kb.charAt(i);
boolean hasDup = false;
for (int j = i + 1; j < kb.length(); j++) {
if (temp == kb.charAt(j)) {
hasDup = true;
kb = removeCharByIndex(kb, j);
j--;
}
}
if (hasDup) {
kb = removeCharByIndex(kb, i);
i--;
}
i++;
}
System.out.println(kb);
}
private static String removeCharByIndex(String str, int index) {
return new StringBuilder(str).deleteCharAt(index).toString();
}
Output: AC
Although, this is not the best and definitely not an efficient solution to this, I think you can get the idea of iterating the input string character by character and removing it if it has duplicates.
The following answer concerns only the transformation of XYYYXACX to ACX. If we wanted to have AC, it's a whole different answer. The other answers already speak about it, and I'll invite you to consult the contains method of String too.
We should consider avoiding -most of the time- modifying the things we iterate. Using a temporary variable could be a kind of solution. To use it, we could change our mindset. Instead of erasing the undesired letters, we can save the ones we want.
To identify the desired character, we need to test if all surrounding letters are different from the tested one. It'll be the opposite of what you did with if(temp == kb.charAt(i+1)) { like if(temp != kb.charAt(i+1)) {. But considering that the tested string will not change anymore, we will need to test the previous letter too as if(temp != kb.charAt(i-1) && temp != kb.charAt(i+1)) {.
As previously said, once we have identified the letter, we will keep the value with a temporary variable. That will lead to replace kb = kb.replace(""+temp, ""); by buffer = buffer + temp; if buffer is our temporary variable initialized with an empty string (Aka. String buffer = "";). In the end, we could override our base value with the temporary one.
At this step, we will have:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
String buffer = "";
int i;
for (i = 1; i < kb.length(); i++) {
char temp = kb.charAt(i);
if(temp != kb.charAt(i-1) && temp != kb.charAt(i+1)) {
buffer = buffer + temp;
}
}
kb = buffer;
System.out.println(kb);
}
That'll sadly not work, trying to access invalid indexes of our string. We should consider two particular behavior for the first and the last letter because they are close to only one letter. For these letters, we will have only one comparison. So, we can make them inside or outside the loop. For clarity, we will do it outside.
For the first one, it will look like to if (kb.charAt(0) != kb.charAt(1)) { and at if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) { for the last. The body of the condition will remain the same as the one in the loop.
Once done, we will reduce the scope of our loop to exclude these character with for (i = 1; i < (kb.length() - 1); i++) {.
Now we will have something working, but only for one iteration:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
String buffer = "";
int i;
if (kb.charAt(0) != kb.charAt(1)) {
buffer = buffer + kb.charAt(0);
}
for (i = 1; i < (kb.length() - 1); i++) {
char temp = kb.charAt(i);
if(temp != kb.charAt(i-1) && temp != kb.charAt(i+1)) {
buffer = buffer + temp;
}
}
if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {
buffer = buffer + kb.charAt(kb.length() - 1);
}
kb = buffer;
System.out.println(kb);
}
XYYYXACX will become XXACX.
Once said, our index problem can occur again if the string has only one letter. However, all of this would have been useless because obviously, we can't have a duplicate letter in this situation. As a fact, we should wrap the whole thing to ensure that we have at least two letters:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
if (kb.length() >= 2) {
String buffer = "";
int i;
if (kb.charAt(0) != kb.charAt(1)) {
buffer = buffer + kb.charAt(0);
}
for (i = 1; i < (kb.length() - 1); i++) {
char temp = kb.charAt(i);
if (temp != kb.charAt(i - 1) && temp != kb.charAt(i + 1)) {
buffer = buffer + temp;
}
}
if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {
buffer = buffer + kb.charAt(kb.length() - 1);
}
kb = buffer;
}
System.out.println(kb);
}
The last thing to do is perform this treatment until we have no more undesired letters. For this task, the do { ... } while ( ... ) seems perfect. We can use for the condition comparison the size of the string. Because when the size of the previous iteration is equal to the temporary variable, we will know that we have finished.
We will need to perform this comparison before affecting the value of our temporary variable to the base one. Otherwise, it'll always be the same.
In the end, the following thing should be a potential solution:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
Boolean modified;
do {
modified = false;
if (kb.length() >= 2) {
String buffer = "";
int i;
if (kb.charAt(0) != kb.charAt(1)) {
buffer = buffer + kb.charAt(0);
}
for (i = 1; i < (kb.length() - 1); i++) {
char temp = kb.charAt(i);
if (temp != kb.charAt(i - 1) && temp != kb.charAt(i + 1)) {
buffer = buffer + temp;
}
}
if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {
buffer = buffer + kb.charAt(kb.length() - 1);
}
modified = (kb.length() != buffer.length());
kb = buffer;
}
} while (modified);
System.out.println(kb);
}
Take note that this code is ugly for the sole purpose of the explanation. We should refactor this code. We can improve it a lot for the sake of brevity and, why not, performance.

Increasing number sequence in a string java

Problem: Check if the numbers in the string are in increasing order.
Return:
True -> If numbers are in increasing order.
False -> If numbers are not in increasing order.
The String sequence are :
CASE 1 :1234 (Easy) 1 <2<3<4 TRUE
CASE 2 :9101112 (Medium) 9<10<11<12 TRUE
CASE 3 :9991000 (Hard) 999<1000 TRUE
CASE 4 :10203 (Easy) 1<02<03 FALSE
(numbers cannot have 0 separated).
*IMPORTANT : THERE IS NO SPACES IN STRING THAT HAVE NUMBERS"
My Sample Code:
// converting string into array of numbers
String[] str = s.split("");
int[] numbers = new int[str.length];
int i = 0;
for (String a : str) {
numbers[i] = Integer.parseInt(a.trim());
i++;
}
for(int j=0;j<str.length;j++)
System.out.print(numbers[j]+" ");
//to verify whether they differ by 1 or not
int flag=0;
for(int j=0;j<numbers.length-1;j++){
int result=Integer.parseInt(numbers[j]+""+numbers[j+1]) ;
if(numbers[j]>=0 && numbers[j]<=8 && numbers[j+1]==numbers[j]+1){
flag=1;
}
else if(numbers[j]==9){
int res=Integer.parseInt(numbers[j+1]+""+numbers[j+2]) ;
if(res==numbers[j]+1)
flag=1;
}
else if(result>9){
//do something
}
}
This is the code I wrote ,but I cant understand how to perform for anything except one-digit-numbers ( Example one-digit number is 1234 but two-digit numbers are 121314). Can anyone have a solution to this problem?. Please share with me in comments with a sample code.
I'm gonna describe the solution for you, but you have to write the code.
You know that the input string is a sequence of increasing numbers, but you don't know how many digits is in the first number.
This means that you start by assuming it's 1 digit. If that fails, you try 2 digits, then 3, and so forth, until you've tried half the entire input length. You stop at half, because anything longer than half cannot have next number following it.
That if your outer loop, trying with length of first number from 1 and up.
In the loop, you extract the first number using substring(begin, end), and parse that into a number using Integer.parseInt(s). That is the first number of the sequence.
You then start another (inner) loop, incrementing that number by one at a time, formatting the number to text using Integer.toString(i), and check if the next N characters of the input (extracted using substring(begin, end)) matches. If it doesn't match, you exit inner loop, to make outer loop try with next larger initial number.
If all increasing numbers match exactly to the length of the input string, you found a good sequence.
This is code for the pseudo-code suggested by Andreas .Thanks for the help.
for (int a0 = 0; a0 < q; a0++) {
String s = in.next();
boolean flag = true;
for (int i = 1; i < s.length() / 2; i++) {
int first = Integer.parseInt(s.substring(0, i));
int k=1;
for (int j = i; j < s.length(); j++) {
if (Integer.toString(first + (k++)).equals(s.substring(j, j + i)))
flag = true;
else{
flag=false;
break;
}
}
if (flag)
System.out.println("YES");
else
System.out.println("NO");
}
I would suggest the following solution. This code generates all substrings of the input sequence, orders them based on their start index, and then checks whether there exists a path that leads from the start index to the end index on which all numbers that appear are ordered. However, I've noticed a mistake (I guess ?) in your example: 10203 should also evaluate to true because 10<203.
import java.util.*;
import java.util.stream.Collectors;
public class PlayGround {
private static class Entry {
public Entry(int sidx, int eidx, int val) {
this.sidx = sidx;
this.eidx = eidx;
this.val = val;
}
public int sidx = 0;
public int eidx = 0;
public int val = 0;
#Override
public String toString(){
return String.valueOf(this.val);
}
}
public static void main(String[] args) {
assert(check("1234"));
assert(check("9101112"));
assert(check("9991000"));
assert(check("10203"));
}
private static boolean check(String seq) {
TreeMap<Integer,Set<Entry>> em = new TreeMap();
// compute all substrings of seq and put them into tree map
for(int i = 0; i < seq.length(); i++) {
for(int k = 1 ; k <= seq.length()-i; k++) {
String s = seq.substring(i,i+k);
if(s.startsWith("0")){
continue;
}
if(!em.containsKey(i))
em.put(i, new HashSet<>());
Entry e = new Entry(i, i+k, Integer.parseInt(s));
em.get(i).add(e);
}
}
if(em.size() <= 1)
return false;
Map.Entry<Integer,Set<Entry>> first = em.entrySet().iterator().next();
LinkedList<Entry> wlist = new LinkedList<>();
wlist.addAll(first.getValue().stream().filter(e -> e.eidx < seq
.length()).collect(Collectors.toSet()));
while(!wlist.isEmpty()) {
Entry e = wlist.pop();
if(e.eidx == seq.length()) {
return true;
}
int nidx = e.eidx + 1;
if(!em.containsKey(nidx))
continue;
wlist.addAll(em.get(nidx).stream().filter(n -> n.val > e.val).collect
(Collectors.toSet()));
}
return false;
}
}
Supposed the entered string is separated by spaces, then the code below as follows, because there is no way we can tell the difference if the number is entered as a whole number.
boolean increasing = true;
String string = "1 7 3 4"; // CHANGE NUMBERS
String strNumbers[] = string.split(" "); // separate by spaces.
for(int i = 0; i < strNumbers.length - 1; i++) {
// if current number is greater than the next number.
if(Integer.parseInt(strNumbers[i]) > Integer.parseInt(strNumbers[i + 1])) {
increasing = false;
break; // exit loop
}
}
if(increasing) System.out.println("TRUE");
else System.out.println("FALSE");

Reduce the value of a letter, e.g. can change 'd' to 'c', but cannot change 'c' to 'd'. In order to form a palindrome

public class Solution {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int tc = Integer.parseInt(br.readLine());//I get Numberformat Exception here
for(int i=0;i<tc;i++) // Even if my inputs are on separate lines
{
String original = br.readLine();
palindrome(original);
}
}
public static void palindrome(String original)
{
String reverse="";
int length = original.length();
for ( int i = length - 1 ; i >= 0 ; i-- )
reverse = reverse + original.charAt(i);
if (original.equals(reverse))
{
System.out.println(0);
}
else
{
char[] org = original.toCharArray();
int len = org.length;
int mid = len / 2;
if(len % 2 == 0)
{
char[] front = new char[mid];
char[] back = new char[mid];
for(int i=0;i<mid;i++)
{
front[i] = org[i];
}
int j=0;
for(int i=len-1;i>=mid;i--)
{
back[j] = org[i];
j++;
while(j > mid)
{
break;
}
}
change(front,back,mid);
}
else
{
char[] front = new char[mid];
char[] back = new char[mid];
for(int i=0;i<mid;i++)
{
front[i] = org[i];
}
int j=0;
for(int i=len-1;i>mid;i--)
{
back[j] = org[i];
j++;
while(j > mid)
{
break;
}
}
change(front,back,mid);
}
}
}
public static void change(char[] front,char[] back,int len)
{
int count =0;
for(int i =0;i<len;i++)
{
if(front[i] != back[i] )
{
count += (back[i] - front[i]);
}
}
System.out.println(count)
}
}
What i try to do here is get an input from the number of test cases say 3 in my first line followed by the test-cases themselves.
sample input :
3
abc
abcba
abcd
Now it has to check if the string is a palindrome if its so it ll print 0
else it breaks the string into two halves front and back and finds the minimum number of changes to make it a palidrome.
here i have also checked if its a odd or even length string if odd i have omitted the middle char.
By changes we can only change 'd' to 'b' not 'b' to 'd'
Once a letter has been changed to 'a', it can no longer be changed.
My code works fine for the above input but it doesnt for some other inputs i dont quiet understand why..
for instance if i give a custom test case as
5
assfsdgrgregedhthtjh
efasfhnethiaoesdfgv
ehadfghsdfhmkfpg
wsertete
agdsjgtukgtulhgfd
I get a Number Format Exception.
Your code works fine here, whithout NumberFormatException: http://ideone.com/QJqjmG
This may not solve your problem, but improves your code...
As first user input you are expecting an integer. You are parsing the String returned by br.readLine() and do not take care of the NumberFormatException parseInt(...) may throw.
Just imagine someone hits space or return key as first input.
So I propose to put a try-catch-block around the parseInt(...). Here is an example how this may look like.
Guys thank you for all your suggestion i just found out why my other test cases weren't working
public static void change(char[] front,char[] back,int len)
{
int count =0;
for(int i =0;i<len;i++)
{
if(front[i] != back[i] )
{
count += (back[i] - front[i]);
}
}
System.out.println(count)
}
This part of my code has to be changed to
public static void change(char[] front,char[] back,int len)
{
int count =0;
for(int i =0;i<len;i++)
{
if(front[i] != back[i] )
{
char great = findGreatest(front[i],back[i]);
if(great == back[i])
{
count += (back[i] - front[i]);
}
else
{
count += (front[i] - back[i]);
}
}
}
System.out.println(count);
}
public static char findGreatest(char first,char second)
{
int great = first;
if(first < second)
{
great = second;
}
return (char)great;
}
Because i get negative values coz of subtracting ascii's which are greater than them and as i have already mentioned i can only do 'd' to 'a' not the other way round.
Thank you for your time guys!

Sequence Count i.e suppose aabbbaaccd is string output should be a=2,b=3,a=2,c=2,d=1

As i have tried ,it gives ArrayIndexOutOfBounds Ecxeption and does not print last char
please help me to find bug in my code.or is there any alternate
public static void sequenceCount(String s) {
int counter;
int i=0;
char c;
char[] arr = s.toCharArray();
while(i<arr.length){
counter=0;
c = arr[i];
while(c==arr[i]){
counter++;
i++;
}
System.out.println("letter"+" "+c+":"+"number of times"+counter);
}
}
As i am a novice to java my code may be inefficient
Your inner loop is not bound by the length of the array. Try:
while(i < arr.length && c==arr[i]){
counter++;
i++;
}
This works - you need to ensure that your inner loop doesn't pass beyond the end of the string, and you need to always catch the last letter, too:
public static void sequenceCount(String s) {
char[] arr = s.toCharArray();
int i = 0, n = arr.length;
while (i < n) {
char c = arr[i];
int count = 0;
do {
++i; ++count;
} while (i < n && arr[i] == c);
System.out.println("letter "+ c +":"+"number of times " + count);
}
}
My approach would be to use two for loops.
The first for loop would run a loop with the decimal equivalent of A to Z.
The second for loop would run a loop that runs through the entire character array/string (I'd prefer a string rather than a char array here) and check to see if that given value at that index is equal the the value ran by the first for loop. If they are equal than add one to count. Print.
Don't forget to reset your counter after every run as well.
Similar Topic can be found here: Counting letters in a string using two for-loops
While many answers here are O(n^2), I tried to do it within O(n) time using recursion. This is modified from existing code that I already had so I know the method returns an int, but I don't use it (it's left over from copied code - fix it as you see fit)
public class CountCharSeqRecursive {
private String test = "AAABBA"; // (3)A(2)B(1)A
private StringBuilder runningString = new StringBuilder();
public static void main(String[] args) {
CountCharSeqRecursive t = new CountCharSeqRecursive();
System.out.println(t.getEncryptedValue(t.test));
}
public String getEncryptedValue(String seq){
int startIndex=0;
this.createCounterSeq(seq.charAt(startIndex), seq, startIndex);
return runningString.toString();
}
private int createCounterSeq(char prev, String sequence, int currentIndex){
return createCounterSeq(prev, sequence, currentIndex, 0);
}
private int createCounterSeq(char prev, String sequence, int currentIndex, int count){
if(currentIndex<sequence.length()){
char current = sequence.charAt(currentIndex);
if((prev^current) < 1){
++count;
}else {
this.addToSequence(count, prev);
count = 1;
}
return count += createCounterSeq(current, sequence, ++currentIndex, count);
}
this.addToSequence(count, prev);
return count;
}
private void addToSequence(int count, char ch){
runningString.append("("+count+")").append(ch);
}
}
My solution using HashSet, Works for all cases of a non-empty string.
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet<Character> set = new HashSet<Character>();
String input = "aabbcdeaab";
set.add(input.charAt(0));
int count = 1;
StringBuilder output = new StringBuilder("");
for(int i=1;i<input.length();i++) {
char next = input.charAt(i);
if(set.contains(next)) {
count++;
}else {
char prev = input.charAt(i-1);
output.append(Character.toString(prev) + count );
set.remove(prev);
set.add(next);
count=1;
}
}
output.append(Character.toString(input.charAt(input.length()-1)) + count );
System.out.println(output.toString());
}

How can i keep track of multiple counter variables

I have written some code that count the number of "if" statements from unknown number of files. How can i keep a count for each file separate and a total of "if" from all files?
code:
import java.io.*;
public class ifCounter4
{
public static void main(String[] args) throws IOException
{
// variable to keep track of number of if's
int ifCount = 0;
for (int c = 0; c < args.length; c++)
{
// parameter the TA will pass in
String fileName = args[c];
// create a new BufferReader
BufferedReader reader = new BufferedReader( new FileReader (fileName));
String line = null;
StringBuilder stringBuilder = new StringBuilder();
String ls = System.getProperty("line.separator");
// read from the text file
while (( line = reader.readLine()) != null)
{
stringBuilder.append(line);
stringBuilder.append(ls);
}
// create a new string with stringBuilder data
String tempString = stringBuilder.toString();
// create one last string to look for our valid if(s) in
// with ALL whitespace removed
String compareString = tempString.replaceAll("\\s","");
// check for valid if(s)
for (int i = 0; i < compareString.length(); i++)
{
if (compareString.charAt(i) == ';' || compareString.charAt(i) == '}' || compareString.charAt(i) == '{') // added opening "{" for nested ifs :)
{
i++;
if (compareString.charAt(i) == 'i')
{
i++;
if (compareString.charAt(i) == 'f')
{
i++;
if (compareString.charAt(i) == '(')
ifCount++;
} // end if
} // end if
} // end if
} // end for
// print the number of valid "if(s) with a new line after"
System.out.println(ifCount + " " + args[c]); // <-- this keeps running total
// but not count for each file
}
System.out.println();
} // end main
} // end class
You can create a Map that stores the file names as keys and the count as values.
Map<String, Integer> count = new HashMap<String, Integer>();
After each file,
count.put(filename, ifCount);
ifcount = 0;
Walk the value set to get the total.
How about a Map which uses the file name as key and keeps the count of ifs as value? For overall count, store it in its own int, or just calculate it when needed by adding up all the values in the Map.
Map<String, Integer> ifsByFileName = new HashMap<String, Integer>();
int totalIfs = 0;
for each if in "file" {
totalIfs++;
Integer currentCount = ifsByFileName.get(file);
if (currentCount == null) {
currentCount = 0;
}
ifsByFileName.put(file, currentCount + 1);
}
// total from the map:
int totalIfsFromMap = 0;
for (Integer fileCount : ifsByFileName.values()) {
totalIfsFromMap += fileCount;
}
Using an array would solve this problem.
int[] ifCount = new int[args.length];
and then in your loop ifCount[c]++;
Problematic in this scenario is when many threads want to increase the same set of counters.
Operations such as ifCount[c]++; and ifsByFileName.put(file, currentCount + 1);are not thread safe.
The obvious solution to use a ConcurrentMap and AtomicLong is also insufficient, since you must place the initial values of 0, which would require additional locking.
The Google Guava project provides a convenient out of the box sollution: AtomicLongMap
With this class you can write:
AtomicLongMap<String> cnts = AtomicLongMap.create();
cnts.incrementAndGet("foo");
cnts.incrementAndGet("bar");
cnts.incrementAndGet("foo");
for (Entry<String, Long> entry : cnts.asMap().entrySet()) {
System.out.println(entry);
}
which prints:
foo=2
bar=1
And is completely thread safe.
This is a counter that adds to 100 and if you edit the value of N it puts a * next to the multiples of.
public class SmashtonCounter_multiples {
public static void main(String[] args) {
int count;
int n = 3; //change this variable for different multiples of
for(count = 1; count <= 100; count++) {
if((count % n) == 0) {
System.out.print(count + "*");
}
else {
System.out.print(count);
if (count < 100) {
System.out.print(",");
}
}
}
}

Categories

Resources