this is my first post on Stack Overflow, so please, be forgiving! :)
I have a list with 403 Polish registration plates symbols and counties. It looks like this:
BIA powiat białostocki
BBI powiat bielski
BGR powiat grajewski
CT Toruń
etc.
I made a code which let me to turn the first space into "=".
import java.io.*;
public class Test {
public static void main(String args[]) {
String Str = new String("BAU powiat augustowski");
System.out.println(Str.replaceFirst(" ", "="));
}
}
How can I make a loop (for? do while?) to change all 403 records? I would appreciate any help. Thank you in advance!
If your list is a List<String>, you can do this :
for (for int i = 0, i < yourList.size(), i++) {
yourList.set(i, yourList.get(i).replaceFirst(" ", "="));
}
Other ways to loop are available here : https://crunchify.com/how-to-iterate-through-java-list-4-way-to-iterate-through-loop/
Best
You COULD also use Stream API. Eg if you want to filter all invalid strings
List<String> registrations = new ArrayList<>(5);
registrations.add("BIA powiat białostocki");
registrations.add("BBI powiat bielski");
registrations.add("BGR powiat grajewski");
registrations.add("BGGHND");
registrations.add("CT Toruń etc.");
registrations = registrations.stream()
.filter(registration -> registration.split(" ").length>1)
.map(registration -> registration.replaceFirst(" ","="))
.collect(Collectors.toList());
Output:
BIA=powiat białostocki
BBI=powiat bielski
BGR=powiat grajewski
CT=Toruń etc.
As you mentioned how can make a loop then I would suggest to learn java loop syntax at first. Following tutorial can be helpful
https://books.trinket.io/thinkjava2/chapter6.html
https://www.codingame.com/playgrounds/6162/6-ways-to-iterate-or-loop-a-map-in-java
Regarding the solution, you can loop your lists using for/while loop or using Stream API. Here is your solution using stream API:
List<String> lists = new ArrayList<>();
lists.add("BAU powiat augustowski");
lists.add("BBI powiat bielski");
lists = lists.stream()
.map(s -> s.replaceFirst("\\s", "="))
.collect(toList());
Either if you are using an ArrayList or a HashSet you can use two ways:
Fyi: assuming your lists name is registrationList and it holds Objects names Registration
Either a for loop:
for(Registration registration : registrationList){
registration.replaceFirst(" ", "=");
}
Or you can use a Stream:
registrationList.stream.forEach(registration-> registration.replaceFirst(" ", "="));
If you have all lines in txt file and you want to modify that by replacing first space to = you could use stream API like:
List<String> collect = Files.lines(Paths.get(PATH_TO_FILE)).stream()
.map(s -> s.replaceFirst(" ", "="))
.collect(toList());
Files.write(PATH_TO_FILE, collect, StandardOpenOption.CREATE);
See more StandardOpenOption
Related
I have a sorted ArrayList A and used streams to group by the substring(3,7). I'm using a large dataset, so I don't know all the different substring(3,7) there are.
ArrayList A for example looks something like this (but with a lot more data): ooo122ppp, aaa122b333, zzz122bmmm, ccc9o9i333, mmm9o9i111, qqqQmQm888, 777QmQmlll, vvvjjj1sss
I need to loop through each group so that I can do something to that grouped data. I've tried for loops, if statements, etc, but can't figure it out. I've tried this, but I get an error regarding the for loop. How am I able to loop through each group I have to perform operations on the Strings in the group?
Collection<List<String>> grouped = A.stream().collect(groupingBy(ex -> ex.substring(3,7))).values();
for(int g=0; g<grouped.forEach(); g++) {
//do something
}
You can use the forEach method on Collection. It should not be confused with a regular for loop.
grouped.forEach(group -> {
group.forEach(str -> {
//do something
});
});
Are you looking for something like this?
List<String> stringList = List.of("ooo122ppp", "aaa122b333", "zzz122bmmm", "ccc9o9i333", "mmm9o9i111", "qqqQmQm888", "777QmQmlll", "vvvjjj1sss");
Map<String, List<String>> collection = stringList.stream().collect(Collectors.groupingBy(ex -> ex.substring(3, 7)));
for (Map.Entry<String, List<String>> entry : collection.entrySet()) {
System.out.println("group <" + entry.getKey() + "> strings " + entry.getValue());
}
Output
group <jjj1> strings [vvvjjj1sss]
group <122b> strings [aaa122b333, zzz122bmmm]
group <122p> strings [ooo122ppp]
group <9o9i> strings [ccc9o9i333, mmm9o9i111]
group <QmQm> strings [qqqQmQm888, 777QmQmlll]
Otherwise please try to better explain the requirement ;)
Currently im learning about stream and want to implement a method which accepts a string. The String starts with a certain word and ends with the same. The given example is "breadtunabread". The method return the word in between the bread.
public String getTopping(String s){
Stream<String> stream = Stream.of(s);
stream.filter(t -> t.startsWith("bread") && t.endsWith("bread")).
forEach(t -> Stream.of(t.split("bread")[1]).collect(Collectors.toList()));
}
I'd like to either save it to a List or change it directly so it returns a String.
Is it possible to get the first value from the stream and not use collect?
I somehow made it work using forEach and adding the value to an ArrayList and returning it but i'd like to know whether there is a way to do it directly using the stream.
Thanks in advance.
And to return just a String:
public String getTopping(String s, String toReplace) {
Stream<String> stream = Stream.of(s);
return stream.filter(t -> t.startsWith(toReplace) && t.endsWith(toReplace))
.findFirst()
.map(t -> t.replaceAll(toReplace, ""))
.orElseThrow(RuntimeException::new);
//.orElseThrow(() -> new NoBreadException("s"));
}
Stream<String> stream = Stream.of("breadtunabread");
List<String> stringList =
stream
.filter(t -> t.startsWith("bread") && t.endsWith("bread"))
.map(t -> (t.split("bread")[1]))
.collect(Collectors.toList());
Is this what you are looking for?
What others mentioned are correct that this is completely unnecessary. I posted this as you have mentioned that yuo are learning streams.
Just like #Naman pointed out, you don't need a Stream for this. String#replaceAll will quite literally replace all instances of the String (bread) with empty String values and in the end you get you're topping. Added the base parameter in case you're a monster like me and eat cheese between pieces of ham.
public static String getTopping(String value, String base) {
return value.replaceAll(base, "");
}
String topping = getTopping("breadtunabread", "bread")
Assuming you have a List of items you want to get the toppings of.
List<String> sandwhiches = Arrays.asList(
"breadtunabread",
"breadchickenbread",
"breadcheesebread",
"breadturkeybread",
"breadlambbread"
);
List<String> toppings = sandwhiches.stream()
.map(sandwhich -> getTopping(sandwhich, "bread"))
.collect(Collectors.toList());
Result
[tuna, chicken, cheese, turkey, lamb]
I'm reading the book 'Java in Action'.
And I saw an example code of Stream in the book.
List<String> names = menu.stream()
.filter(d -> {
System.out.println("filtering" + d.getName());
return d.getCalories() > 300;
})
.map(d -> {
System.out.println("mapping" + d.getName());
return d.getName();
})
.limit(3)
.collect(toList());
When the code is executed, the result is as follows.
filtering __1__.
mapping __1__.
filtering __2__.
mapping __2__.
filtering __3__.
mapping __3__.
That is, because of limit(3), the log message is printed only 3 times!
In this book, this is called in "loop fusion."
But, I don't understand this.
Because, if you know whether an object is filtered, you have to calculate the filtering function. Then, "filtering ..." message is should be printed, I think.
Please, Explain me about how the loop fusion works internally.
“Because, if you [want to] know whether an object is filtered, you have to calculate the filtering function”, is right, but perhaps your sample data wasn’t sufficient to illustrate the point. If you try
List<String> result = Stream.of("java", "streams", "are", "great", "stuff")
.filter(s -> {
System.out.println("filtering " + s);
return s.length()>=4;
})
.map(s -> {
System.out.println("mapping " + s);
return s.toUpperCase();
})
.limit(3)
.collect(Collectors.toList());
System.out.println("Result:");
result.forEach(System.out::println);
it will print
filtering java
mapping java
filtering streams
mapping streams
filtering are
filtering great
mapping great
Result:
JAVA
STREAMS
GREAT
Showing that
In order to find three elements matching the filter, you might have to evaluate more than three elements, here, four element are evaluated, but you don’t need to evaluate subsequent elements once you have three matches
The subsequent mapping function only need to be applied to matching elements. This allows to conclude that it is irrelevant whether .map(…).limit(…)or .limit(…).map(…) was specified.
This differs from the relative position of .filter and .limit which is relevant.
The term “loop fusion” implies that there is not a filtering loop, followed by a mapping loop, followed by a limit operation, but only one loop (conceptionally), performing the entire work, equivalent to the following single loop:
String[] source = { "java", "streams", "are", "great", "stuff"};
List<String> result = new ArrayList<>();
int limit = 3;
for(String s: source) {
System.out.println("filtering " + s);
if(s.length()>=4) {
System.out.println("mapping " + s);
String t = s.toUpperCase();
if(limit-->0) {
result.add(t);
}
else break;
}
}
I think you got it wrong. limit is actually called short-circuiting (because it is executed only 3 times).
While loop fusion is filter and map executed at a single pass. These two operations where merged into a single one that is executed at each element.
You do not see output like this:
filtering
filtering
filtering
mapping
mapping
mapping
Instead you see filter followed immediately by a map; so these two operations were merged into a single one.
Generally you should not care how that is done internally (it build a pipeline of these operations), because this might change and it is implementation specific.
I am using streams to concatenate a series of strings and add commas between them, but there must be no comma at the beginning or the end of the result string.
import java.util.Arrays;
import java.util.List;
public class QuestionNine {
public static void main(String[] args) {
new QuestionNine().launch();
}
public void launch(){
List<String> words = Arrays.asList("Hello", "Bonjour", "engine", "Hurray", "What",
"Dog", "boat", "Egg", "Queen", "Soq", "Eet");
String result = (words.stream().map(str -> str + ",").reduce("", (a,b) -> a + b));
result = result.substring(0, result.length() -1); //removes last comma
System.out.println(result);
}
}
Instead of using the String.substring() method at the end to get rid of the last comma, is there a way i could have deleted the last comma within the stream pipeline?
The usual idiom is to use the joining Collector with Streams.
String res = words.stream().collect(Collectors.joining(","));
Although you can use String.join in your case since you are directly dealing with an Iterable.
String res = String.join(",", words);
The problem with your approach is that the mapping function you apply impose that there will be a comma at the end of each word. You could get rid of this mapping; and apply the reduce function such that you get the desired output:
.stream().reduce("", (a,b) -> a.isEmpty() ? b : a+","+b);
but I don't recommend this.
Yes, you can use Collectors.joining() here:
String joined = words.stream().collect(Collectors.joining(", "));
Or, also as noted from comments, you can use newly added String.join(CharSequence, Iterable) method.
String joined = String.join(", ", words);
Assume I have a set of numbers like 1,2,3,4,5,6,7 input as a single String. I would like to convert those numbers to a List of Long objects ie List<Long>.
Can anyone recommend the easiest method?
You mean something like this?
String numbers = "1,2,3,4,5,6,7";
List<Long> list = new ArrayList<Long>();
for (String s : numbers.split(","))
list.add(Long.parseLong(s));
System.out.println(list);
Since Java 8 you can rewrite it as
List<Long> list = Stream.of(numbers.split(","))
.map(Long::parseLong)
.collect(Collectors.toList());
Little shorter versions if you want to get List<String>
List<String> fixedSizeList = Arrays.asList(numbers.split(","));
List<String> resizableList = new ArrayList<>(fixedSizeList);
or one-liner
List<String> list = new ArrayList<>(Arrays.asList(numbers.split(",")));
Bonus info:
If your data may be in form like String data = "1, 2 , 3,4"; where comma is surrounded by some whitespaces, the split(",") will produce as result array like ["1", " 2 ", " 3", "4"].
As you see second and third element in that array contains those extra spaces: " 2 ", " 3" which would cause Long.parseLong to throw NumberFormatException (since space is not proper numerical value).
Solution here is either:
using String#trim on those individual elements before parsing like Long.parseLong(s.trim())
consuming those extra whitespace along , while splitting. To do that we can use split("\\s*,\\s*") where
\s (written as "\\s" in string literals) represents whitespace
* is quantifier representing zero or more
so "\\s*" represents zero or more whitespaces (in other words makes it optional)
Simple and handy solution using java-8 (for the sake of completion of the thread):
String str = "1,2,3,4,5,6,7";
List<Long> list = Arrays.stream(str.split(",")).map(Long::parseLong).collect(Collectors.toList());
System.out.println(list);
[1, 2, 3, 4, 5, 6, 7]
Even better, using Pattern.splitAsStream():
Pattern.compile(",").splitAsStream(str).map(Long::parseLong).collect(Collectors.toList());
String input = "1,2,3,4,5,6,7";
String[] numbers = input.split("\\,");
List<Integer> result = new ArrayList<Integer>();
for(String number : numbers) {
try {
result.add(Integer.parseInt(number.trim()));
} catch(Exception e) {
// log about conversion error
}
}
You can use String.split() and Long.valueOf():
String numbers = "1,2,3,4,5,6,7";
List<Long> list = new ArrayList<Long>();
for (String s : numbers.split(","))
list.add(Long.valueOf(s));
System.out.println(list);
If you're not on java8 and don't want to use loops, then you can use Guava
List<Long> longValues = Lists.transform(Arrays.asList(numbersArray.split(",")), new Function<String, Long>() {
#Override
public Long apply(String input) {
return Long.parseLong(input.trim());
}
});
As others have mentioned for Java8 you can use Streams.
List<Long> numbers = Arrays.asList(numbersArray.split(","))
.stream()
.map(String::trim)
.map(Long::parseLong)
.collect(Collectors.toList());
I've used the following recently:
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
...
final ImmutableList<Long> result = Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.splitToStream(value)
.map(Long::valueOf)
.collect(toImmutableList());
This uses Splitter from Guava (to handle empty strings and whitespaces) and does
not use the surprising String.split().
I would use the excellent google's Guava library to do it. String.split can cause many troubles.
String numbers="1,2,3,4,5,6,7";
Iterable<String> splitIterator = Splitter.on(',').split(numbers);
List<String> list= Lists.newArrayList(splitIterator );