I'm working on an assignment and the code is working perfectly, except for one small little problem.
For example at the end it's supposed to say, "t,e,s,t" but instead it prints out "t,e,s,t,".
import javax.swing.JOptionPane;
public class program {
public static int divide(String input) {
int length=0;
String output = "";
for (int i=0; i<input.length(); i++) {
length++;
output += input.charAt(i);
output += ",";
}
JOptionPane.showMessageDialog(null, "Eingabe: " +input+ "\nAnzahl der Zeichen: " +length+ "\nZeichen: \n" +output);
return length;
}
}
I'd just like to say for those who are planning on giving tips, please note that this is an assignment so I'm not really allowed to make any MAJOR changes to it. This program HAS to be:
Solved using a for loop
The output HAS to be in JOptionPane
It has to be in a method (for I'll have to write an inputDialog later in the main-method, but that's unimportant right now).
My only problem with it, for example the output would have to say (I'll translate the output in english, since I'm at an austrian school) if the string was "hello", the program would say "letters: h, e, l, l, o" but instead it says "h, e, l, l, o," with a comma at the end, how do I get rid of it?
(Also sorry if I messed up any variables, I renamed them all from German to English for this post so I hope I didn't mess any of them up)
One of the possibilities is to print comma not after each character, but before every character except the first one.
You only want to add a comma if there is more data to come after. So you can do it in two ways:
add the comma before the text if there's already something in the string:
String output = "";
for (int i=0; i<input.length(); i++) {
length++;
if (output.length() > 0) output += ",";
output += input.charAt(i);
}
add the comma after unless it's the last element:
String output = "";
for (int i=0; i<input.length(); i++) {
length++;
output += input.charAt(i);
if (i < input.length() - 1) output += ",";
}
Personally I like the first way.
Hello I used an if statement to check if we are at the first letter and if we are then we don't write a comma, here is the code:
{
int length = 0;
String output = "";
int i = 0;
for ( i = 0; i < input.length(); i++)
{
if(i == 0)
{
length++;
output += input.charAt(i);
}
else
{
output += ",";
output += input.charAt(i);
length++;
}
}
Console.WriteLine(output);
}
You only need to check whether you are at the last character, and if you are, then break out from the loop.
for (int i=0; i<input.length(); i++) {
length++; //you do not seem to need this
output += input.charAt(i);
if (i==(input.length()-1)) break; //checking whether we are at the last character
output += ",";
}
Two additional notes:
Please follow the Java Naming Conventions and use PascalCase for your class names, this is very important;
It would make your code much more efficient if you'd use StringBuilder instead of String, to concatenate characters and dynamically build your string. String is immutable and per each concatenation, you're actually creating a new instance of it, which is expensive.
I would use a StringJoiner
StringJoiner output = new StringJoiner(",");
for (int i = 0; i < input.length(); i++) {
length++;
output.add(input.substring(i, i + 1));
}
JOptionPane.showMessageDialog(… + output.toString());
so You are not allowed to use StringJoiner
String output = "";
String comma = "";
for (int i = 0; i < input.length(); i++) {
length++;
output += comma;
output += input.charAt(i);
comma = ",";
}
Your loop iterates through every character, appending both the character and a comma. This includes the final character. You need to find a way to avoid adding a comma after the final iteration;
Using String and appending characters one by one is very inefficient. This is what StringBuilder is designed for;
What is the purpose of the length variable? It can be replaced with input.length() - 1;
String.format() makes your code easier to read rather than chaining together string concatenations;
Don't be afraid to use a healthy amount of spacing throughout your code. It is much harder to read otherwise.
Something like this should work well:
public static int divide(String input) {
int length = input.length() - 1;
String output;
if (input.length() == 0) output = input;
else {
StringBuilder outputBuilder = new StringBuilder(input.charAt(0));
for (int i = 1; i < input.length(); i++) {
outputBuilder.append(',').append(input.charAt(i));
}
output = outputBuilder.toString();
}
JOptionPane.showMessageDialog(null, String.format("Eingabe: %s\nAnzahl der Zeichen: %d\nZeichen: \n%s", input, length, output);
return length;
}
Once you learn, use a StringJoiner or similar modern device. My favourite link is at the bottom. There will also be a time when you learn to use a StringBuilder or StringBuffer for assembling a string.
In the meantime, I still like what I learned in my first year of programming: when one iteration through a loop needs to be different, take it out of the loop. In my experience this often gives the clearest code. In your case:
String input = "Wort";
String output = "" + input.charAt(0);
for (int i = 1; i < input.length(); i++) {
output += ",";
output += input.charAt(i);
}
System.out.println("Zeichen: " + output);
Output:
Zeichen: W,o,r,t
In this case I have taken the first character outside the loop and add it to the output (without comma) already in the declaration of output. The loop starts from index 1 (not 0). Inside the loop I have to add the comma before adding the next char. In other cases one may put the last item outside the loop instead, the result will be the same.
Links
Answer by Lii to a similar question demonstrating all of String.join(), Collectors.joining() and StringJoiner.
StringBuilder vs String concatenation in toString() in Java
Related
I wrote a method to reduce a sequence of the same characters to a single character as follows. It seems its logic is correct while there is a room for improvement in terms of performance, according to my tutor. Could anyone shed some light on this?
Comments of aspects other than performance is also really appreciated.
public class RemoveRepetitions {
public static String remove(String input) {
String ret = "";
String last = "";
String[] stringArray = input.split("");
for(int j=0; j < stringArray.length; j++) {
if (! last.equals(stringArray[j]) ) {
ret += stringArray[j];
}
last = stringArray[j];
}
return ret;
}
public static void main(String[] args) {
System.out.println(RemoveRepetitions.remove("foobaarrbuzz"));
}
}
We can improve the performance by using StringBuilder instead of using string as string operations are costlier. Also, the split function is also not required (it will make the program slower as well).
Here is a way to solve this:
public static String remove(String input)
{
StringBuilder answer = new StringBuilder("");
int N = input.length();
int i = 0;
while (i < N)
{
char c = input.charAt(i);
answer.append( c );
while (i<N && input.charAt(i)==c)
++i;
}
return answer.toString();
}
The idea is to iterate over all characters of the input string and keep appending every new character to the answer and skip all the same consecutive characters.
Possible change which you could think of in your code is:
Time Complexity: Your code is achieving output in O(n) time complexity, which might be the best possible way.
Space Complexity: Your code is using extra memory space which arises due to splitting.
Question to ask: Can you achieve this output, without using the extra space for character array that you get after splitting the string? (as character by character traversal is possible directly on string).
I can provide you the code here but, it would be great if you could try it on your own, once you are done with your attempts
you can lookup for the best solution here (you are almost there)
https://www.geeksforgeeks.org/remove-consecutive-duplicates-string/
Good luck!
As mentioned before, it is much better to access the characters in the string using method String::charAt or at least by iterating a char array retrieved with String::toCharArray instead of splitting the input string into String array.
However, Java strings may contain characters exceeding basic multilingual plane of Unicode (e.g. emojis 😂😍😊, Chinese or Japanese characters etc.) and therefore String::codePointAt should be used. Respectively, Character.charCount should be used to calculate appropriate offset while iterating the input string.
Also the input string should be checked if it's null or empty, so the resulting code may look like this:
public static String dedup(String str) {
if (null == str || str.isEmpty()) {
return str;
}
int prev = -1;
int n = str.length();
System.out.println("length = " + n + " of [" + str + "], real length: " + str.codePointCount(0, n));
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; ) {
int cp = str.codePointAt(i);
if (i == 0 || cp != prev) {
sb.appendCodePoint(cp);
}
prev = cp;
i += Character.charCount(cp); // for emojis it returns 2
}
return sb.toString();
}
A version with String::charAt may look like this:
public static String dedup2(String str) {
if (null == str || str.isEmpty()) {
return str;
}
int n = str.length();
StringBuilder sb = new StringBuilder(n);
sb.append(str.charAt(0));
for (int i = 1; i < n; i++) {
if (str.charAt(i) != str.charAt(i - 1)) {
sb.append(str.charAt(i));
}
}
return sb.toString();
}
The following test proves that charAt fails to deduplicate repeated emojis:
System.out.println("codePoint: " + dedup ("😂😂😍😍😊😊😂 hello"));
System.out.println("charAt: " + dedup2("😂😂😍😍😊😊😂 hello"));
Output:
length = 20 of [😂😂😍😍😊😊😂 hello], real length: 13
codePoint: 😂😍😊😂 helo
charAt: 😂😂😍😍😊😊😂 helo
I am a beginner in Java. Below is a piece of code that I wrote. the purpose is to add a space between each upper case character for example for string="ILoveMyDog" to
"I Love My Dog". However the outcome of this code is "ILoveMy Dog". Can someone help me figured out what is wrong? upperCaseList is another method to extract the index of all uppercase character and put them into a list, and I am pretty sure that part is correct
for (int i = 0; i < upperCaseList.size(); i++) {
newStr = w.replace(w.substring(upperCaseList.get(i), upperCaseList.get(i)+1), " "+ w.substring(upperCaseList.get(i), upperCaseList.get(i)+1));
}
return newStr
It is because you are overwriting the result of String.replace by reassigning newStr inside the loop. So, you only see the effect of the last replacement.
Assuming the content of upperCaseList is 1, 5, 7.
After Loop iteration 1, newStr has I LoveMyDog
After Loop iteration 2, newStr has ILove MyDog (you are not using the previous result, rather using the original string)
After Loop iteration 3, newStr has ILoveMy Dog
Try this,
String newStr = w;
for (int i = 0; i < upperCaseList.size(); i++) {
newStr = newStr.replace(w.substring(upperCaseList.get(i), upperCaseList.get(i)+1), " "+ w.substring(upperCaseList.get(i), upperCaseList.get(i)+1));
}
There are plenty of ways to solve this though. Rather than storing the list of indices containing the uppercase characters and using String.substring and String.replace, you can use StringBuilder to build up the string from the original string by looping through the characters and checking if it is upper/lower cased.
StringBuilder resultBuilder = new StringBuilder();
for (int i = 1; i < w.length(); i++) { //Note: Starting at index 1
if (Character.isUpperCase(w.charAt(i))) {
resultBuilder.append(" ")
.append(w.charAt(i));
} else {
resultBuilder.append(w.charAt(i));
}
}
System.out.println(resultBuilder.toString());
Not sure how you are creating the upperCaseList, I suggest to have one loop for all. Hope below code will work for your requirement.
public void test(){
String str ="ILoveMyDog";
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i< str.length() ; i++) {
if(Character.isUpperCase(str.charAt(i))){
//add space
strBuilder.append(" ");
}
//add the orginal char
strBuilder.append(str.charAt(i));
}
//use toString method
System.out.println(strBuilder.toString());
}
So, I need to write a program using loops that takes a string and counts what and how many letters appear in that string. (string "better butter" would print "b appears 2 times, e appears 3 times, ' '(space) appears 1 time, and so on). While I understand the idea and concept behind this assignment, actually pulling it off has been rough.
My nested for loop is where the problems are coming from, I assume. What I've written only loops once (i think) and just shows the first character and says there's only one of that character.
Edit: Preferably without using Map or arrays. I'm fine with using them if it's the only way, but they've not been covered in my class so I'm trying to avoid them. Every other similar question to this (that I've found) uses Map or array.
import java.util.Scanner;
class myString{
String s;
myString() {
s = "";
}
void setMyString(String s) {
this.s = s;
}
String getMyString() {
return s;
}
String countChar(String s){
s = s.toUpperCase();
int cnt = 0;
char c = s.charAt(cnt);
for (int i = 0; i <= s.length(); i++)
for (int j = 0; j <= s.length(); j++) //problem child here
c = s.charAt(cnt);
cnt++;
if (cnt == 1)
System.out.println(c+" appears "+cnt+" time in "+s);
else
System.out.println(c+" appears "+cnt+" times in "+s);
return "for"; //this is here to prevent complaint from the below end bracket.
}
}
public class RepeatedCharacters {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s;
System.out.println("Enter a sentence: ");
s = in.nextLine();
myString myS = new myString();
// System.out.println(myS.getMyString());
// System.out.println(myS.countChar());
myS.countChar(s);
}
}
First you will need to scan the entire string and store the
counts of each characters. Later you can just print the counts.
Algorithm 1:
Use a HashMap to store the character as key and its count as value. (If you are new to Java, you might want to read up on
HashMaps.)
Every time you read a character in your for loop, check if it present in the HashMap. If yes, then increment the count by 1. Else
add a new characters to the map with count 1.
Printing:
Just iterate on your HashMap and print out the character and
their respective counts.
Issue with your code: You are trying to print the count as soon as you
read a character. But the character might appear again later in the
string. So you need to keep track of the characters you have already
read.
Algorithm 2:
String countChar(String s){
has_processed = []
for i = 0 to n
cnt = 0
if s.charAt(i) has been processed
continue;
for j = i+1 to n
if (s.charAt(i) == s.charAt(j))
cnt++
add s.charAt(i) to has_processed array
print the count of s.charAt(i)
}
Use a frequency array to get an answer in linear time.
/* package whatever; // don't place package name! */
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
String s = "better butter";
int freq[] = new int[26];
int i;
for (i = 0; i < s.length(); i++) {
if (s.charAt(i) >= 'a' && s.charAt(i) <= 'z')
freq[s.charAt(i)-'a']++;
}
for (i = 0; i < freq.length; i++) {
if (freq[i] == 0) continue;
System.out.println((char)(i+'a') + " appears " + freq[i] + " times" );
}
}
}
Ideone Link
Note that this can be expanded to include uppercase letters, but for demonstrative purposes, only lowercase letters are handled in the above code.
EDIT: While the OP did ask if it was possible to do this without an array, I would recommend against such. That solution would have terrible time complexity and repeat character counts (unless an array is used to keep track of seen characters, which is counter to the aim). Thus, the above solution is the best way to do it in a reasonable amount of time (linear) with limited space consumption.
I would do the following. Create a HashMap which keeps track of which unique characters are in the string and the count for each character.
You only need to iterate over the string once, and put each character into the HashMap. if the characer is in the map, icrement the integer count in the map, else add 1 to the map for that character. Print out the map with toString() to get the result. The whole thing can be done in about 4 lines of code.
The only thing being done in your nested for loop with the following
c = s.charAt(cnt)
is setting the c char to the value of the first letter (i.e. index 0 of the string) over and over and over until you've looped through the string n^2 times. In other words, you're not incrementing your cnt counter within the for loops at all.
Suggestion: try to use meaningful names for your variables; it will help you a lot in your career. Also class names should always start with a capital letter.
Although it is not the quickest solution in terms of performance, the most simple solution should be:
import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);
Source: Most efficient way to increment a Map value in Java
Please next time use the search function before posting a new question.
Here is my version of countChar(String s)
boolean countChar(String s) {
if(s==null) return false;
s = s.toUpperCase();
//view[x] will means that the characted in position x has been just read
boolean[] view = new boolean[s.length()];
/*
The main idea is:
foreach character c = s.charAt(x) in the string s, I have a boolean value view[x] which say if I have already examinated c.
If c has not been examinated yet, I search for other characters equals to c in the rest of the string.
When I found other characters equals to c, I mark it as view and I increment count with count++.
*/
for (int i = 0; i < s.length(); i++) {
if (!view[i]) {
char tmp = s.charAt(i);
int count = 0;
for (int j = i; j < s.length(); j++) {
if (!view[j] && s.charAt(j) == tmp) {
count++;
view[j] = true;
}
}
System.out.println("There were " + count + " " + tmp);
}
}
return true;
}
It should work, excuse me for my English because I'm italian
Hi I need some help with my internship test task. I've read it a couple of times and I can't even say that I'm surely know what I need to do. So the task is:
You must generate a string of random length consisting of random ASCII characters. After that you need to replace all entries of '1' with 'Q' and 'Q' with '1' in it. To complete the task you are allowed to change one substring with another as many times as you want. For example as a result of changing "ww" -> "wert" the string "wwwwwQ" will become "wertwertwQ". Write a program which does required changes in the most optimal way (doing minimum amount of replacements).
I've already implemented string generation and i simply don't know what to do next. As said in the header, i need to do this using Java. Could you please offer me some way to solve this task?
As i said what i've already done is genString() which generates a char array for me and a replace() method which does what intended but it not uses substrings so it seems that this task should be done another way.
public static char[] genString()
{
int n = rand.nextInt(50) + 1;
char[] arr = new char[n];
for (int i = 0; i<n; i++)
{
arr[i] = (char)(rand.nextInt(95) + 33);
}
return arr;
}
public static void replace(char[] arr)
{
for (int i = 0; i < arr.length; i++)
{
arr[i] = (arr[i] == 'Q') ? '1'
: (arr[i] == '1' ) ? 'Q'
: arr[i];
}
}
What i actually don't understand is that how the substrings could be used there. I don't understand how going from "wwwwwQ" to "wertwertwQ" -like replacements will help me replace the 'Q' in it
To make the fewest replacements, I would use a stream-based approach:
StringBuilder sb = new StringBuilder(str);
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == 'Q') {
sb.setCharAt(i, '1');
} else if (str.charAt(i) == '1') {
sb.setCharAt(i, 'Q');
}
}
return sb.toString();
Using a StringBuilder make manipulating the string a lot more efficient, because a new string doesn't have to be created every time you change a letter.
well, try this code:
public String Replace(String theString, String first, String Second)
{
String result ="";
result=theString.replace(first, Second);
return result;
}
Example of using:
System.out.println(Replace("wwwwwQ", "ww", "wert"));// result will be: wertwertwQ
I hope it will help you, best regards
i'm doing an encoding program where i'm supposed to delete every character in the string which appears twice. i've tried to traverse through the string but it hasn't worked. does anyone know how to do this? Thanks.
public static String encodeScrambledAlphabet(String str)
{
String newword = str;
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
newword += alphabet;
newword = newword.toUpperCase();
for (int i = 0, j = newword.length(); i < newword.length() && j >=0; i++,j--)
{
char one = newword.charAt(i);
char two = newword.charAt(j);
if (one == two)
{
newword = newword.replace(one, ' ');
}
}
newword = newword.replaceAll(" ", "");
return newword;
}
Assuming that you would like to keep only the first occurrence of the character, you can do this:
boolean seen[65536];
StringBuilder res = new StringBuilder();
str = str.toUpperCase();
for (char c : str.toCharArray()) {
if (!seen[c]) res.append(c);
seen[c] = true;
}
return res.toString();
The seen array contains flags, one per character, indicating that we've seen this character already. If your characters are all ASCII, you can shrink the seen array to 128.
Assuming by saying deleting characters that appears twice, you mean AAABB becomes AAA, below code should work for you.
static String removeDuplicate(String s) {
StringBuilder newString = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
String s1 = s.substring(i, i + 1);
// We need deep copy of original String.
String s2 = new String(s);
// Difference in size in two Strings gives you the number of
// occurences of that character.
if(s.length() - s2.replaceAll(s1, "").length() != 2)
newString.append(s1);
}
return newString.toString();
}
Efficiency of this code is arguable :) It might be better approach to count the number of occurences of character by a loop.
So, from the code that you've shown, it looks like you aren't comparing every character in the string. You are comparing the first and last, then the second and next to last. Example:
Here's your string:
THISISTHESTRINGSTRINGABCDEFGHIJKLMNOPQRSTUVWXYZ
First iteration, you will be comparing the T at the beginning, and the Z at the end.
Second iteration, you will be comparing the H and the Y.
Third: I and X
etc.
So the T a the beginning never gets compared to the rest of the characters.
I think a better way to do this would be to to do a double for loop:
int length = newword.length(); // This way the number of iterations doesn't change
for(i = 0; i < length; i++){
for(j = 0; j < length; j++){
if(i!=j){
if(newword.charAt(i) == newword.charAt(j)){
newword.replace(newword.charAt(i), ' ');
}
}
}
}
I'm sure that's not the most efficient algorithm for it, but it should get it done.
EDIT: Added an if statement in the middle, to handle i==j case.
EDIT AGAIN: Here's an almost identical post: function to remove duplicate characters in a string