Reducing For loops - Code Optimization - java

I am new to Java and looking for code optimization techniques. This code is giving the expected output using two for loops and I want to reduce/eliminate the loops (if possible).
public static void main(String[] args) {
String dummy = "Hello how are you";
String[] strArr = dummy.split(" ");
for(int i=0; i < strArr.length;i++){
String word = strArr[i];
for(int j=word.length(); j > 0; j--){
System.out.print(word.charAt(j - 1));
}
System.out.print(" ");
}
}
Output: olleH woh era uoy
Please advice.

Since the complexity of printing the output would remain the same, all you can do is to "hide" the loops into existing methods that do what you want, not eliminate them, because the amount of work the system needs to perform remains the same in terms of the number of characters that need to be processed.
You can hide the nested loop by using string reversal technique described in this Q&A. The outer loop can be pushed into String.join:
String[] data = {"quick", "brown", "fox"};
System.out.println(
String.join(
" "
, Arrays.stream(data)
.map(s -> new StringBuilder(s).reverse().toString())
.toArray(String[]::new)
)
);
Demo.

As I stated in my comment the complexity is already O(n) and you cannot get better because you need to read the input string. Anyway, there is a way to "unnest" the two for loops: reverse the whole string, split on spaces, and reverse the resulting array.
Example:
"Hello how are you"
"uoy era woh olleH"
"uoy" "era" "woh" "olleH"
"olleH" "woh" "era" "uoy"
Code:
public static void main(String[] args) {
String dummy = "Hello how are you";
int n = dummy.length();
StringBuilder sb = new StringBuilder(n);
while (--n >= 0) sb.append(dummy.charAt(n));
String[] tokens = sb.toString().split(" ");
n = tokens.length;
while (--n >= 0) System.out.print(tokens[n] + " ");
}
Instead, if you are after cools java 8 Stream tricks, read dasblinkenlight's answer.

This is another simple solution using StringTokenizer.
It takes only O(n).
public static void main(String[] args) {
System.out.println(reverseStringWordByWord("Hello How are you"));
}
public static String reverseStringWordByWord(String input) {
StringBuilder result = new StringBuilder();
StringTokenizer sToken = new StringTokenizer(input, " ");
while (sToken.hasMoreTokens()) {
StringBuilder thisToken = new StringBuilder(sToken.nextToken());
result.append(thisToken.reverse() + " ");
}
return result.toString();
}

Related

How to add spaces to left and right of chars in a string?

I'm trying to print out a string with spaces on either side of each char in the string
so if I have
String s = "abcde"
it would create something like this
a b c d e
with a space before the first char and three between each char.
I just haven't been able to find a way to do this with my knowledge.
Update
Updated requirement:
I failed to realize that I need something that add one place in front
of the first term and then 3 spaces between each term.
_0___0___0___0___0_ for example.
For the updated requirement, you can use yet another cool thing, String#join.
public class Main {
public static void main(String[] args) {
String s = "abcde";
String result = "_" + String.join("___", s.split("")) + "_";
System.out.println(result);
}
}
Output:
_a___b___c___d___e_
Original answer
There can be so many ways to do it. I find it easier to do it using Regex:
public class Main {
public static void main(String[] args) {
String s = "abcde";
String result = s.replaceAll(".", " $0 ");
System.out.println(result);
}
}
Output:
a b c d e
The Regex, . matches a single character and $0 replaces this match with space + match + space.
Another cool way is by using Stream API.
import java.util.Arrays;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
String s = "abcde";
String result = Arrays.stream(s.split(""))
.map(str -> " " + str + " ")
.collect(Collectors.joining());
System.out.println(result);
}
}
Output:
a b c d e
A super simple example, that doesn't handle a multitude of potential input scenarios.
public static void main(String[] args)
{
String s = "abcde";
for (int i = 0; i < s.length(); ++i) {
System.out.print("_" + s.charAt(i));
}
System.out.println("_");
}
NOTE: used an underscore rather than a space in order to allow visual check of the output.
Sample output:
_a_b_c_d_e_
Rather than direct output, one could use a StringBuilder and .append to a builder instead, for example.
Using StringBuilder:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); ++i) {
sb.append('_').append(s.charAt(i));
}
sb.append('_');
System.out.println(sb.toString());
Based on a comment where the desired output is slightly different (two internal spaces, one leading and trailing space), this suggests an alternative approach:
public static String addSpace(String inp) {
StringBuilder sB = new StringBuilder();
String string = inp.trim();
String div = "___"; // spaces, or whatever
sB.append('_'); // add leading space
for(int index = 0; index < string.length(); ++index) {
sB.append(string.charAt(index))
.append(div); // two spaces
}
sB.setLength(sB.length() - (div.length() - 1) );
return (sB.toString());
}
NOTE: again using an underscore to allow for easier debugging.
Output when div is set to 3 underscores (or spaces):
_0___0___0___1___0___1___1___0_
You can define an empty string : result = “”;
Then go through the string you want to print with foreach loop With the function toCharArray()
(char character : str.toCharArray())
And inside this loop do ->
result += “ “ + character;
String result = s.chars().mapToObj(
Character::toString
).collect(Collectors.joining(" "));
Similar to the loop versions, but uses a Stream.
Another one liner to achieve this, by splitting the String into String[] of characters and joining them by space:
public class Main {
public static void main(String[] args) {
String s = "abcde";
System.out.println(" " + String.join(" ", s.split("")) + " ");
}
}
Output:
a b c d e
Edit:
The above code won't work for strings with Unicode codepoints like "👦ab😊", so instead of splitting on empty string, the split should be performed on regex: "(?<=.)".
public class Main {
public static void main(String[] args) {
String s = "abcde";
System.out.println(" " + String.join(" ", s.split("(?<=.)")) + " ");
}
}
Thanks to #saka1029 for pointing this out.
You can use Collectors.joining(delimiter,prefix,suffix) method with three parameters:
String s1 = "abcde";
String s2 = Arrays.stream(s1.split(""))
.collect(Collectors.joining("_+_", "-{", "}-"));
System.out.println(s2); // -{a_+_b_+_c_+_d_+_e}-
See also: How to get all possible combinations from two arrays?

(Java) Append elements to a String variable from String array up to specific index?

public class sample {
public static void main(String[] args) {
String[] test = new String[1024];
int count = 0;
test[count] = "33";
count++;
test[count] = "34";
String s = new String();
This is just a simplified version, but I would like to append elements to a String variable s up to the index value of count without using StringBuilder, is there a way to do it? Thank you.
edit: without using loop as well, is there a String manipulation function I can use?
One way to do that is using String.join and Arrays.copyOf:
String s = String.join("", Arrays.copyOf(test, count + 1));
Which, with your test data, produces 3334
Dont quite understand what you want...
But I guess you could user char array?
char[] c = new char[maxCount]
for(int i = 0;i<maxCount;i++){
c[i] = "34";
}
String s = String.valueOf(c)
Hope this could help you:)
Hard to say, what you're asking for...
Concatenating consecutive numbers could be easily done with a stream:
String s = IntStream.rangeClosed(0, 1024)
.mapToObj(Integer::toString)
.collect(Collectors.joining());
We can use the join function of String.
Assuming the test is the string array.
String joinedString = String.join("", Arrays.stream(test).limit(count).collect(Collectors.toList()))
You can use String.join()
public class Main {
public static void main(String[] args) {
String[] test = new String[1024];
int count = 0;
test[count] = "33";
count++;
test[count] = "34";
String s = new String();
System.out.println(s=String.join("", Arrays.copyOf(test, count + 1)));
}
}

Formatting ArrayList of Integers

I currently have this code, but when it's printed, it prints the integers on different lines one right after another. How can I format this, so that it prints like this: [1, 2, 4, 5, 6]
I've tried converting the ArrayList to an array, but I must not being doing it correctly because it doesn't seem to be working.
import java.util.*;
public class PartBMod {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Enter your array of integers (using commas to separate them, no spaces): ");
ArrayList<Integer> arrayList = new ArrayList<Integer>();
String str = new String(input.nextLine());
String[] array = str.split(",");
for (String i : array) {
arrayList.add(Integer.parseInt(i));
}
Collections.sort(arrayList);
for (Integer i : arrayList) {
System.out.println(i);
}
}
Your desired format is the default for ArrayList. You could use
System.out.println(arrayList);
Elliot's answer notwithstanding, if toString() didn't happen to be what you wanted, you could roll your own in just one statement:
System.out.println(
Arrays.stream(input.nextLine().split(","))
.mapToInt(Integer::parseInt)
.sort()
.mapToObj(String::valueOf)
.collect(Collectors.join(",", "[", "]")));
Do this simple way:
System.out.print("[");
for (int i = 0; i < arrayList.size(); i++) {
System.out.print(arrayList.get(i));
if(i != arrayList.size()-1)
System.out.print(", ");
}
System.out.println("]");
The reason your code isn't working the way you want it to is because you are using println. It will add a new line after printing each item. What I would do is something like this
String textToPrint = "[ ";
for (int i =0; i < arrayList.size(); i++) {
textToPrint += String.valueOf(i) + ", ";
}
textToPrint = textToPrint.substring(0, textToPrint.length() -2);
textToPrint += "]";
System.out.println(textToPrint);
This way you are adding each integer into a string to print.
Elliots' answer is cleaner and more efficient in this case, but this loop may be needed in other cases.
The substring line removes a trailing comma and space. IMO it's cleaner than adding an if statement into the for loop.

Split string after every 2 words and store into list

I have a string of words as follows:
String words = "disaster kill people action scary seriously world murder loose world";
Now, I wish to split every 2 words and store them into a list so that it will produce something like:
[disaster kill, people action, scary seriously,...]
The problem with my code is that it will split whenever it encounters a space. How do I modify it so that it will only be added into the list if it only encounters every 2nd space, preserving the space after each word)
My code:
ArrayList<String> wordArrayList = new ArrayList<String>();
for(String word : joined.split(" ")) {
wordArrayList.add(word);
}
Thanks.
Use this regular expression: (?<!\\G\\S+)\\s.
PROOF:
String words = "disaster kill people action scary seriously world murder loose world";
String[] result = words.split("(?<!\\G\\S+)\\s");
System.out.printf("%s%n", Arrays.toString(result));
And the result:
[disaster kill, people action, scary seriously, world murder, loose world]
Your loop should leave you with an ArrayList<String> that has each word, right? All you need to do now is iterate through that list and combine words together in sets of twos.
ArrayList<String> finalList = new ArrayList<String>();
for (int i = 0; i < wordArrayList.Size(); i+=2) {
if (i + 1 < wordArrayList.Size()
finalList.add(wordArrayList.get(i) + " " + wordArrayList.get(i + 1);
}
This should take your split words and add them to the list with spaces so that they look like your desired output.
I was looking for splitting a string after 'n' words.
So I modify the above solution.
private void spiltParagraph(int splitAfterWords, String someLargeText) {
String[] para = someLargeText.split(" ");
ArrayList<String> data = new ArrayList<>();
for (int i = 0; i < para.length; i += splitAfterWords) {
if (i + (splitAfterWords - 1) < para.length) {
StringBuilder compiledString = new StringBuilder();
for (int f = i; f <= i + (splitAfterWords - 1); f++) {
compiledString.append(para[f] + " ");
}
data.add(compiledString.toString());
}
}
}
I run into this problem today, adding an extra difficulty that is to write this solution in Scala. So, I needed to write a recursive solution that looks like:
val stringToSplit = "THIS IS A STRING THAT WE NEED TO SPLIT EVERY 2 WORDS"
#tailrec
def obtainCombinations(
value: String,
elements: List[String],
res: List[String]
): List[String] = {
if (elements.isEmpty)
res
else
obtainCombinations(elements.head, elements.tail, res :+ value + ' ' + elements.head)
}
obtainCombinations(
stringToSplit.split(' ').head,
stringToSplit.split(' ').toList.tail,
List.empty
)
The output will be:
res0: List[String] = List(THIS IS, IS A, A STRING, STRING THAT, THAT WE, WE NEED, NEED TO, TO SPLIT, SPLIT EVERY, EVERY 2, 2 WORDS)
Porting this to Java would be:
String stringToSplit = "THIS IS A STRING THAT WE NEED TO SPLIT EVERY 2 WORDS";
public ArrayList<String> obtainCombinations(String value, List<String> elements, ArrayList<String> res) {
if (elements.isEmpty()) {
return res;
} else {
res.add(value + " " + elements.get(0));
return obtainCombinations(elements.get(0), elements.subList(1, elements.size()), res);
}
}
ArrayList<String> result =
obtainCombinations(stringToSplit.split(" ")[0],
Arrays.asList(stringToSplit.split(" ")),
new ArrayList<>());

How to Check for Deleted Words Between 2 Sentences in Java

What's the best approach in Java if you want to check for words that were deleted from sentence A in sentence B. For example:
Sentence A: I want to delete unnecessary words on this simple sentence.
Sentence B: I want to delete words on this sentence.
Output: I want to delete (unnecessary) words on this (simple) sentence.
where the words inside the parenthesis are the ones that were deleted from sentence A.
Assuming order doesn't matter: use commons-collections.
Use String.split() to split both sentences into arrays of words.
Use commons-collections' CollectionUtils.addAll to add each array into an empty Set.
Use commons-collections' CollectionUtils.subtract method to get A-B.
Assuming order and position matters, this looks like it would be a variation of the Longest Common Subsequence problem, a dynamic programming solution.
wikipedia has a great page on the topic, there's really too much for me to outline here
http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
Everyone else is using really heavy-weight algorithms for what is actually a very simple problem. It could be solved using longest common subsequence, but it's a very constrained version of that. It's not a full diff; it only includes deletes. No need for dynamic programming or anything like that. Here's a 20-line implementation:
private static String deletedWords(String s1, String s2) {
StringBuilder sb = new StringBuilder();
String[] words1 = s1.split("\\s+");
String[] words2 = s2.split("\\s+");
int i1, i2;
i1 = i2 = 0;
while (i1 < words1.length) {
if (words1[i1].equals(words2[i2])) {
sb.append(words1[i1]);
i2++;
} else {
sb.append("(" + words1[i1] + ")");
}
if (i1 < words1.length - 1) {
sb.append(" ");
}
i1++;
}
return sb.toString();
}
When the inputs are the ones in the question, the output matches exactly.
Granted, I understand that for some inputs there are multiple solutions. For example:
a b a
a
could be either a (b) (a) or (a) (b) a and maybe for some versions of this problem, one of these solutions is more likely to be the "actual" solution than the other, and for those you need some recursive or dynamic programming approach... but let's not make it too much more complicated than what Israel Sato originally asked for!
String a = "I want to delete unnecessary words on this simple sentence.";
String b = "I want to delete words on this sentence.";
String[] aWords = a.split(" ");
String[] bWords = b.split(" ");
List<String> missingWords = new ArrayList<String> ();
int x = 0;
for(int i = 0 ; i < aWords.length; i++) {
String aWord = aWords[i];
if(x < bWords.length) {
String bWord = bWords[x];
if(aWord.equals(bWord)) {
x++;
} else {
missingWords.add(aWord);
}
} else {
missingWords.add(aWord);
}
}
This works well....for updated strings also
updated strings enclosed with square brackets.
import java.util.*;
class Sample{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
String str1 = sc.nextLine();
String str2 = sc.nextLine();
List<String> flist = Arrays.asList(str1.split("\\s+"));
List<String> slist = Arrays.asList(str2.split("\\s+"));
List<String> completedString = new ArrayList<String>();
String result="";
String updatedString = "";
String deletedString = "";
int i=0;
int startIndex=0;
int endIndex=0;
for(String word: slist){
if(flist.contains(word)){
endIndex = flist.indexOf(word);
if(!completedString.contains(word)){
if(deletedString.isEmpty()){
for(int j=startIndex;j<endIndex;j++){
deletedString+= flist.get(j)+" ";
}
}
}
startIndex=endIndex+1;
if(!deletedString.isEmpty()){
result += "("+deletedString.substring(0,deletedString.length()-1)+") ";
deletedString="";
}
if(!updatedString.isEmpty()){
result += "["+updatedString.substring(0,updatedString.length()-1)+"] ";
updatedString="";
}
result += word+" ";
completedString.add(word);
if(i==slist.size()-1){
endIndex = flist.size();
for(int j=startIndex;j<endIndex;j++){
deletedString+= flist.get(j)+" ";
}
startIndex = endIndex+1;
}
}
else{
if(i == 0){
boolean boundaryCheck = false;
for(int j=i+1;j<slist.size();j++){
if(flist.contains(slist.get(j))){
endIndex=flist.indexOf(slist.get(j));
boundaryCheck=true;
break;
}
}
if(!boundaryCheck){
endIndex = flist.size();
}
if(!completedString.contains(word)){
for(int j=startIndex;j<endIndex;j++){
deletedString+= flist.get(j)+" ";
}
}
startIndex = endIndex+1;
}else if(i == slist.size()-1){
endIndex = flist.size();
if(!completedString.contains(word)){
for(int j=startIndex;j<endIndex;j++){
deletedString+= flist.get(j)+" ";
}
}
startIndex = endIndex+1;
}
updatedString += word+" ";
completedString.add(word);
}
i++;
}
if(!deletedString.isEmpty()){
result += "("+deletedString.substring(0,deletedString.length()-1)+") ";
}
if(!updatedString.isEmpty()){
result += "["+updatedString.substring(0,updatedString.length()-1)+"] ";
}
System.out.println(result);
}
}
This is basically a differ, take a look at this:
diff
and the root algorithm:
Longest common subsequence problem
Here's a sample Java implementation:
http://introcs.cs.princeton.edu/java/96optimization/Diff.java.html
which compares lines. The only thing you need to do is split by word instead of by line or alternatively put each word of both sentences in a separate line.
If e.g. on Linux, you can actually see the results of the latter option using diff program itself before you even write any code, try this:
$ echo "I want to delete unnecessary words on this simple sentence."|tr " " "\n" > 1
$ echo "I want to delete words on this sentence."|tr " " "\n" > 2
$ diff -uN 1 2
--- 1 2012-10-01 19:40:51.998853057 -0400
+++ 2 2012-10-01 19:40:51.998853057 -0400
## -2,9 +2,7 ##
want
to
delete
-unnecessary
words
on
this
-simple
sentence.
The lines with - in front are different (alternatively, it would show + if the lines were added into sentence B that were not in sentence A). Try it out to see if that fits your problem.
Hope this helps.

Categories