how to store the values that has the same parent key - java

lets say I have:
bob:V
bob:A
bob:B
bob:C
bob:C
sally:B
sally:C
sally:A
steve:A
steve:B
steve:C
how do I store:
the values as:
bob={V,A,B,C,C}, sally={B,C,A}, steve={A,B,C}
and for any guy who has a sequence A,B,C repeated how do I get that person name?
I am fairly new to Java and Im trying to implement this scenario, as I dont see anything like this in this communtiy.
here is my final answer: first stored the list into a map and then used collectors to loop through and map it to their respective attributes.
public class Solution{
static List<String> doWork(List<LogItem> eventsInput) {
Map<String, String> personMap = eventsInput.stream()
.collect(Collectors.toMap(LogItem::getUserId, p -> Character.toString(p.getEventChar()), String::concat));
System.out.println("person map is \n" + personMap);
BiPredicate<Entry<String, List<String>>, String> contains =
(entry, attr) -> entry.getValue().stream()
.collect(Collectors.joining()).contains(attr);
String attributes = "ABC";
List<String> results = personMap.entrySet().stream()
.filter(e -> e.getValue().contains(attributes))
.map(Entry::getKey).collect(Collectors.toList());
return results;
}
public static void main(String[] args){
List<LogItem> exampleInputItems = new ArrayList<>();
exampleInputItems.add(new LogItem("bob", 'V'));
exampleInputItems.add(new LogItem("bob", 'A'));
exampleInputItems.add(new LogItem("steve", 'A'));
exampleInputItems.add(new LogItem("bob", 'B'));
exampleInputItems.add(new LogItem("bob", 'C'));
exampleInputItems.add(new LogItem("bob", 'C'));
exampleInputItems.add(new LogItem("steve", 'B'));
exampleInputItems.add(new LogItem("sally", 'B'));
exampleInputItems.add(new LogItem("steve", 'C'));
exampleInputItems.add(new LogItem("sally", 'C'));
exampleInputItems.add(new LogItem("sally", 'A'));
List<String> returnedNames = doWork(exampleInputItems);
if (returnedNames.size() != 2) {
throw new RuntimeException("Wrong number of names found. Found: " + returnedNames);
}
if (!returnedNames.contains("bob")) {
throw new RuntimeException("Did not find \"bob\" in the returnedNames: " + returnedNames);
}
if (!returnedNames.contains("steve")) {
throw new RuntimeException("Did not find \"steve\" in the returnedNames: " + returnedNames);
}
System.out.println("The example passed.");
}
static class LogItem {
public String userId;
public char eventChar;
public LocalDateTime dateTime;
LogItem(String userId, char eventChar) {
this.userId = userId;
this.eventChar = eventChar;
dateTime = LocalDateTime.now();
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public char getEventChar() {
return eventChar;
}
public void setEventChar(char eventChar) {
this.eventChar = eventChar;
}
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
#Override
public String toString() {
return "LogItem [userId=" + userId + ", eventChar=" + eventChar + ", dateTime=" + dateTime + ", getUserId()="
+ getUserId() + ", getEventChar()=" + getEventChar() + ", getDateTime()=" + getDateTime() + "]";
}
}
}
}

First, I would store the attributes in a Map<String,String>. This will make it easier to filter the attributes later. I am using a record in lieu of a class but a class would work as well.
record Person(String getName, String getAttribute) {
}
Create the list of Person objects
List<Person> list = List.of(new Person("bob", "V"),
new Person("bob", "A"), new Person("bob", "B"),
new Person("bob", "C"), new Person("bob", "C"),
new Person("sally", "B"), new Person("sally", "C"),
new Person("sally", "A"), new Person("steve", "A"),
new Person("steve", "B"), new Person("steve", "C"));
Now create the map. Simply stream the list of people and concatenate the attributes for each person.
Map<String, String> personMap = list.stream()
.collect(Collectors.toMap(Person::getName,
Person::getAttribute, String::concat));
The map will look like this.
bob=VABCC
steve=ABC
sally=BCA
Now grab the name based on an attribute string.
Now stream the entries of the map and pass the entry whose value contains the attribute string. Then retrieve the key (name) and return as a list of names.
String attributes = "ABC";
ListString> results = personMap.entrySet().stream()
.filter(e -> e.getValue().contains(attributes))
.map(Entry::getKey).collect(Collectors.toList());
System.out.println(results);
prints
[bob, steve]
Alternative approach using Map<String, List<String>>
Group the objects by name but the values will be a list of attributes instead of a string.
Map<String, List<String>> personMap = list.stream()
.collect(Collectors.groupingBy(Person::getName,
Collectors.mapping(Person::getAttribute,
Collectors.toList())));
The map will look like this.
bob=[V, A, B, C, C]
steve=[A, B, C]
sally=[B, C, A]
To facilitate testing the attributes, a BiPredicate is used to stream the list and concatinate the attributes and then check and see if it contains the attribute string.
BiPredicate<Entry<String, List<String>>, String> contains =
(entry, attr) -> entry.getValue().stream()
.collect(Collectors.joining()).contains(attr);
As before, stream the entry set of the map and apply the filter to pass those entries which satisfy the condition. In this case, the filter invokes the BiPredicate.
String attributes = "ABC";
List<String> results = personMap.entrySet().stream()
.filter(e->contains.test(e, attributes))
.map(Entry::getKey).collect(Collectors.toList());
System.out.println(results);
prints
[bob, steve]
Update Answer
To work with Character attributes, you can do the following using the first example.
Map<String, String> personMap2 = list.stream()
.collect(Collectors.toMap(Person::getName,
p -> Character.toString(p.getAttribute()),
String::concat));
Imo, it would be easier, if possible to change your attribute types to string.

Related

How do I create a code segment for a pipeline to return duplicate Strings in Java 8

I'm creating a pipeline in Java 8 that is able to take a list of students and the name of their clubs that they joined. I want to generate a list of students that joined multiple clubs, so basically I need to return the duplicate names of students that joined 2 or more clubs.
This is my data.txt file where the pipeline is getting the required data.
ARTS:Joey Solydan:Economics
MUSIC:Joey Solydan:Economics
ARTS:Haley Wolloims:WomenStudies
SPORTS:Godfriey Lemonsquesser:Cookery
LITERATURECLUB:Say Moniki:Archeology
FILM:Milles Spielberg:Masscom
ARTS:Milles Spielberg:Masscom
Basically, I need the code to return the names of Joey Solydan and Milles Spielberg because they are in 2 clubs unlike the other names.
So the next step is to create a method to look for Joey and Milles using Java 8 lambda and I started off the method as such:
public static void displayDuplicateNames(ArrayList<Member> member) {
Map<String, Map<String, Set<Member>>> allMembers = new TreeMap<>();
//For loop to initialize the Map
for (Member members : member) {
String club = members.getClub();
String name = members.getName();
Map<String, Set<Member>> clubList = allMembers.computeIfAbsent(org, k -> new TreeMap<>());
Set<Member> nameList = clubList.computeIfAbsent(name, k -> new TreeSet<>());
nameList.add(members);
}
next is to create the pipeline and it follows just after the for loop:
allMembers
.forEach(
(club, clubList) -> {
System.out.printf("\n*** Club %s:", club);
clubList
.forEach(
(name, nameList) ->{
System.out.printf("\n** Member's Name with Multiple Clubs: %s\n", name);
// I got stumped on what to do next
}
);
});
I want an expected output of something like:
*** Club Arts:
** Member's Name with Multiple Clubs: Joey Solydan
** Member's Name with Multiple Clubs: Milles Spielberg
*** Club Music:
** Member's Name with Multiple Clubs: Joey Solydan
*** Club Film:
** Member's Name with Multiple Clubs: Milles Spielberg
UPDATE:
I've followed the code segment from WJS and the current code looks like this:
public static List<String> displayDuplicateNames(ArrayList<Member> member) {
Map<String, Integer> dups = new HashMap<>();
Map<String, Map<String, Set<Member>>> allMembers = new TreeMap<>();
for (Member members : member) {
String org = members.getOrg();
String name = members.getName();
Map<String, Set<Member>> orgList = allMembers.computeIfAbsent(org, k -> new TreeMap<>());
Set<Member> nameList = orgList.computeIfAbsent(name, k -> new TreeSet<>());
nameList.add(members);
}
allMembers
.forEach(
(org, orgList) -> {
System.out.printf("\n*** Organization %s: ", org);
orgList
.forEach(
(name, nameList) ->{
System.out.printf("\n** Member's Name with Multiple Organizations: %s\n", name);
if (dups.containsKey(name)) {
dups.put(name, dups.get(name) + 1);
}
else {
dups.put(name, 1);
}
}
);
});
return dups.entrySet().stream().filter(e -> e.getValue() > 1).map(
Map.Entry::getKey).collect(Collectors.toList());
}
My method for reading the data from the file has been updated as well:
public static List<Member> readDataFromFile(String filename)
{
String line = "";
ArrayList<Member> memberList = new ArrayList<>();
try {
Scanner scanner = new Scanner(new File(filename));
while (scanner.hasNextLine()) {
line = scanner.nextLine();
String[] parts = line.split("[:]");
String club = parts[1];
String name = parts[2];
String course = parts[0];
memberList.add(new Member(club,name,course));
}
} catch (FileNotFoundException ex) {
System.out.println("ERROR, FILE NOT FOUND.");
}
return memberList;
}
The current output is now:
It shows the members of each club but now I need to just show each club with a member that is joined with another club other than theirs.
Here is one possibility.
Put this at the top of the dups method.
Map<String, Integer> dups = new HashMap<>();
Add the following code at your comment
if (dups.containsKey(name)) {
dups.put(name, dups.get(name) + 1);
}
else {
dups.put(name, 1);
}
and return this at the end of the method.
return dups.entrySet().stream().filter(e -> e.getValue() > 1).map(
Entry::getKey).collect(Collectors.toList());
Finally, make certain your displayDups method returns a List<String>
Here is your modified method.
public static List<String> displayDuplicateNames(List<Member> members) {
Map<String, Map<String, Set<Member>>> allMembers = new TreeMap<>();
Map<String, Integer> dups = new HashMap<>();
// For loop to initialize the Map
for (Member member : members) {
String club = member.getClub();
String name = member.getName();
Map<String, Set<Member>> clubList =
allMembers.computeIfAbsent(club, k -> new TreeMap<>());
Set<Member> nameList =
clubList.computeIfAbsent(name, k -> new TreeSet<>());
nameList.add(member);
}
allMembers.forEach((club, clubList) ->
{
System.out.printf("%n*** Club %s:%n", club);
clubList.forEach((name, nameList) ->
{
System.out.printf(" ** Member's Name with Multiple Clubs:
%s%n",
name);
if (dups.containsKey(name)) {
dups.put(name, dups.get(name) + 1);
}
else {
dups.put(name, 1);
}
});
});
return dups.entrySet().stream().filter(e -> e.getValue() > 1).map(
Entry::getKey).collect(Collectors.toList());
}
}
It returns
dups = [Joey Solydan, Milles Spielberg]
Note that I had to make my own Member class since it was not included in your question. So I added getters to retrieve the values.
If you don't mind processing you input twice you could first count how often your students enrolled. Assuming you have all your students in the list memberList:
Map<string, Long> counted = memberList.stream()
.map(member -> member.getName())
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Now counted contains all names once with a count of how often they came up.
Next you could take your memberList and do
stream().filter(member -> counted[member.name] > 1)
That (after collecting yada yada) should result in a list which only contains Members enrolled in more than one club.
Hope the syntax is correct. Didn't code Java for a while now :-)

Return keys with values from Rest API

I want to return JSON from Rest API endpoint as keys with values. Example:
{
"terminal 1":
{"date":"2018-10-06T00:00:00.000+0000","volume":111,"count":1},
"terminal 2":
{"date":"2018-11-06T00:00:00.000+0000","volume":122,"count":1}
}
How I can add the keys? It should be I suppose like this:
List<String<List<TopTerminalsDTO>>>>
Can you give me some code example?
Latest attempt to clean the final code:
#GetMapping("/terminals")
public ResponseEntity<Map<Integer, List<TopTerminalsDTO>>> getTopTerminalsVolumes(
#RequestParam(value = "start_date", required = true) String start_date,
#RequestParam(value = "end_date", required = true) String end_date) {
LocalDateTime start_datel = LocalDateTime.now(Clock.systemUTC());
LocalDateTime end_datel = LocalDateTime.now(Clock.systemUTC());
final List<PaymentTransactionsDailyFacts> list = dashboardRepository.top_daily_transactions(start_datel, end_datel);
final Collector<PaymentTransactionsDailyFacts, List<TopTerminalsDTO>, List<TopTerminalsDTO>> terminalsCollector =
Collector.of(
ArrayList::new,
(terminals, p) -> terminals.add(mapper.toTopTerminalsDTO(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
}
);
final Map<Integer, List<TopTerminalsDTO>> final_map =
list.stream()
.filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(p -> p.getTerminal_id(), terminalsCollector));
return ResponseEntity.ok(final_map);
}
Following your JSON, testDate() should return Map<String, TopTerminalsDTO> instead of List.
Map<String, TopTerminalsDTO> result = newHashMap();
for (int i = 0; i <= 10; i++) {
TopTerminalsDTO ttDto = new TopTerminalsDTO();
ttDto.setCount(ThreadLocalRandom.current().nextInt(20, 500 + 1));
LocalDate localDate = LocalDate.now().minus(Period.ofDays((new Random().nextInt(365 * 70))));
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
ttDto.setDate(date);
ttDto.setVolume(ThreadLocalRandom.current().nextInt(300, 5000 + 1));
result.put("terminal "+i, ttDto)
}
return result;
And, of course, change response type of rest method to ResponseEntity<Map<String, TopTerminalsDTO>>
This is what a Javascript dictionary looks like.
In Java, the correct representation is a Map<String, TopTerminalDto>.
Say you have an ordered List, and you want to return a Map with generated keys terminal{index}.
final List<TopTerminalDto> list = ...
final Map<String, TopTerminalDto> map =
IntStream.range(0, list.size())
.boxed()
.collect(toMap(i -> "terminal" + i, i -> list.get(i)));
The Spring endpoint would become:
#GetMapping("terminals")
Map<String, TopTerminalDto> getTopTerminalVolumes() { ... }
The ResponseEntity is not mandatory in Spring.
Remember to work as much as possible via Stream(s), to produce results without intermediate temporary state.
Additional example:
final List<PaymentTransactionsDailyFacts> list =
dashboardRepository.top_daily_transactions(start_datel, end_datel);
final Map<String, TopTerminalDto> map =
list.stream()
.collect(toMap(p -> p.getTerminal(), this::toDto))
// Conversion method
private TopTerminalDto toDto(final PaymentTransactionsDailyFacts p) {
// Implement conversion to Dto
}
For multiple values associated with a terminal:
final Map<Integer, List<TopTerminalDto>> map =
list.stream()
.filter(p -> p.getTerminal() != null)
.collect(groupingBy(
p -> p.getTerminal(),
Collector.of(
ArrayList::new,
(terminals, p) -> terminals.add(toDto(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
}
)
));
You can clean the code by extracting the Collector.
final Collector<Integer, List<TopTerminalDto>, List<TopTerminalDto>> terminalsCollector =
Collector.of(
ArrayList::new,
(terminals, p) -> terminals.add(toDto(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
}
)
final Map<Integer, List<TopTerminalDto>> map =
list.stream()
.filter(p -> p.getTerminal() != null)
.collect(groupingBy(p -> p.getTerminal(), terminalsCollector));

Serialized form data in Java [duplicate]

I've got the URI like this:
https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback
I need a collection with parsed elements:
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
To be exact, I need a Java equivalent for the C#/.NET HttpUtility.ParseQueryString method.
If you are looking for a way to achieve it without using an external library, the following code will help you.
public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
You can access the returned Map using <map>.get("client_id"), with the URL given in your question this would return "SS".
UPDATE URL-Decoding added
UPDATE As this answer is still quite popular, I made an improved version of the method above, which handles multiple parameters with the same key and parameters with no value as well.
public static Map<String, List<String>> splitQuery(URL url) throws UnsupportedEncodingException {
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
final String[] pairs = url.getQuery().split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new LinkedList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
query_pairs.get(key).add(value);
}
return query_pairs;
}
UPDATE Java8 version
public Map<String, List<String>> splitQuery(URL url) {
if (Strings.isNullOrEmpty(url.getQuery())) {
return Collections.emptyMap();
}
return Arrays.stream(url.getQuery().split("&"))
.map(this::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new, mapping(Map.Entry::getValue, toList())));
}
public SimpleImmutableEntry<String, String> splitQueryParameter(String it) {
final int idx = it.indexOf("=");
final String key = idx > 0 ? it.substring(0, idx) : it;
final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null;
return new SimpleImmutableEntry<>(
URLDecoder.decode(key, StandardCharsets.UTF_8),
URLDecoder.decode(value, StandardCharsets.UTF_8)
);
}
Running the above method with the URL
https://stackoverflow.com?param1=value1&param2=&param3=value3&param3
returns this Map:
{param1=["value1"], param2=[null], param3=["value3", null]}
org.apache.http.client.utils.URLEncodedUtils
is a well known library that can do it for you
import org.apache.hc.client5.http.utils.URLEncodedUtils
String url = "http://www.example.com/something.html?one=1&two=2&three=3&three=3a";
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), Charset.forName("UTF-8"));
for (NameValuePair param : params) {
System.out.println(param.getName() + " : " + param.getValue());
}
Outputs
one : 1
two : 2
three : 3
three : 3a
If you are using Spring Framework:
public static void main(String[] args) {
String uri = "http://my.test.com/test?param1=ab&param2=cd&param2=ef";
MultiValueMap<String, String> parameters =
UriComponentsBuilder.fromUriString(uri).build().getQueryParams();
List<String> param1 = parameters.get("param1");
List<String> param2 = parameters.get("param2");
System.out.println("param1: " + param1.get(0));
System.out.println("param2: " + param2.get(0) + "," + param2.get(1));
}
You will get:
param1: ab
param2: cd,ef
use google Guava and do it in 2 lines:
import java.util.Map;
import com.google.common.base.Splitter;
public class Parser {
public static void main(String... args) {
String uri = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
String query = uri.split("\\?")[1];
final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(query);
System.out.println(map);
}
}
which gives you
{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
The shortest way I've found is this one:
MultiValueMap<String, String> queryParams =
UriComponentsBuilder.fromUriString(url).build().getQueryParams();
UPDATE: UriComponentsBuilder comes from Spring. Here the link.
For Android, if you are using OkHttp in your project. You might get a look at this. It simple and helpful.
final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
final String target = url.queryParameter("target");
final String id = url.queryParameter("id");
}
PLAIN Java 11
Given the URL to analyse:
URL url = new URL("https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback");
This solution collects a list of pairs:
List<Map.Entry<String, String>> list = Pattern.compile("&")
.splitAsStream(url.getQuery())
.map(s -> Arrays.copyOf(s.split("=", 2), 2))
.map(o -> Map.entry(decode(o[0]), decode(o[1])))
.collect(Collectors.toList());
This solution on the other hand collects a map (given that in a url there can be more parameters with same name but different values).
Map<String, List<String>> list = Pattern.compile("&")
.splitAsStream(url.getQuery())
.map(s -> Arrays.copyOf(s.split("=", 2), 2))
.collect(groupingBy(s -> decode(s[0]), mapping(s -> decode(s[1]), toList())));
Both the solutions must use an utility function to properly decode the parameters.
private static String decode(final String encoded) {
return Optional.ofNullable(encoded)
.map(e -> URLDecoder.decode(e, StandardCharsets.UTF_8))
.orElse(null);
}
On Android, there is a Uri class in package android.net . Note that Uri is part of android.net, whereas URI is part of java.net .
Uri class has many functions to extract key-value pairs from a query.
Following function returns key-value pairs in the form of HashMap.
In Java:
Map<String, String> getQueryKeyValueMap(Uri uri){
HashMap<String, String> keyValueMap = new HashMap();
String key;
String value;
Set<String> keyNamesList = uri.getQueryParameterNames();
Iterator iterator = keyNamesList.iterator();
while (iterator.hasNext()){
key = (String) iterator.next();
value = uri.getQueryParameter(key);
keyValueMap.put(key, value);
}
return keyValueMap;
}
In Kotlin:
fun getQueryKeyValueMap(uri: Uri): HashMap<String, String> {
val keyValueMap = HashMap<String, String>()
var key: String
var value: String
val keyNamesList = uri.queryParameterNames
val iterator = keyNamesList.iterator()
while (iterator.hasNext()) {
key = iterator.next() as String
value = uri.getQueryParameter(key) as String
keyValueMap.put(key, value)
}
return keyValueMap
}
If you are using servlet doGet try this
request.getParameterMap()
Returns a java.util.Map of the parameters of this request.
Returns:
an immutable java.util.Map containing parameter names as keys and parameter values as map values. The keys in the parameter map are of type String. The values in the parameter map are of type String array.
(Java doc)
Netty also provides a nice query string parser called QueryStringDecoder.
In one line of code, it can parse the URL in the question.
I like because it doesn't require catching or throwing java.net.MalformedURLException.
In one line:
Map<String, List<String>> parameters = new QueryStringDecoder(url).parameters();
See javadocs here: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html
Here is a short, self contained, correct example:
import io.netty.handler.codec.http.QueryStringDecoder;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
public class UrlParse {
public static void main(String... args) {
String url = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
QueryStringDecoder decoder = new QueryStringDecoder(url);
Map<String, List<String>> parameters = decoder.parameters();
print(parameters);
}
private static void print(final Map<String, List<String>> parameters) {
System.out.println("NAME VALUE");
System.out.println("------------------------");
parameters.forEach((key, values) ->
values.forEach(val ->
System.out.println(StringUtils.rightPad(key, 19) + val)));
}
}
which generates
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
If you're using Java 8 and you're willing to write a few reusable methods, you can do it in one line.
private Map<String, List<String>> parse(final String query) {
return Arrays.asList(query.split("&")).stream().map(p -> p.split("=")).collect(Collectors.toMap(s -> decode(index(s, 0)), s -> Arrays.asList(decode(index(s, 1))), this::mergeLists));
}
private <T> List<T> mergeLists(final List<T> l1, final List<T> l2) {
List<T> list = new ArrayList<>();
list.addAll(l1);
list.addAll(l2);
return list;
}
private static <T> T index(final T[] array, final int index) {
return index >= array.length ? null : array[index];
}
private static String decode(final String encoded) {
try {
return encoded == null ? null : URLDecoder.decode(encoded, "UTF-8");
} catch(final UnsupportedEncodingException e) {
throw new RuntimeException("Impossible: UTF-8 is a required encoding", e);
}
}
But that's a pretty brutal line.
There a new version of Apache HTTP client - org.apache.httpcomponents.client5 - where URLEncodedUtils is now deprecated. URIBuilder should be used instead:
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.net.URIBuilder;
private static Map<String, String> getQueryParameters(final String url) throws URISyntaxException {
return new URIBuilder(new URI(url), StandardCharsets.UTF_8).getQueryParams()
.stream()
.collect(Collectors.toMap(NameValuePair::getName,
nameValuePair -> URLDecoder.decode(nameValuePair.getValue(), StandardCharsets.UTF_8)));
}
A ready-to-use solution for decoding of URI query part (incl. decoding and multi parameter values)
Comments
I wasn't happy with the code provided by #Pr0gr4mm3r in https://stackoverflow.com/a/13592567/1211082 . The Stream-based solution does not do URLDecoding, the mutable version clumpsy.
Thus I elaborated a solution that
Can decompose a URI query part into a Map<String, List<Optional<String>>>
Can handle multiple values for the same parameter name
Can represent parameters without a value properly (Optional.empty() instead of null)
Decodes parameter names and values correctly via URLdecode
Is based on Java 8 Streams
Is directly usable (see code including imports below)
Allows for proper error handling (here via turning a checked exception UnsupportedEncodingExceptioninto a runtime exception RuntimeUnsupportedEncodingException that allows interplay with stream. (Wrapping regular function into functions throwing checked exceptions is a pain. And Scala Try is not available in the Java language default.)
Java Code
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import static java.util.stream.Collectors.*;
public class URIParameterDecode {
/**
* Decode parameters in query part of a URI into a map from parameter name to its parameter values.
* For parameters that occur multiple times each value is collected.
* Proper decoding of the parameters is performed.
*
* Example
* <pre>a=1&b=2&c=&a=4</pre>
* is converted into
* <pre>{a=[Optional[1], Optional[4]], b=[Optional[2]], c=[Optional.empty]}</pre>
* #param query the query part of an URI
* #return map of parameters names into a list of their values.
*
*/
public static Map<String, List<Optional<String>>> splitQuery(String query) {
if (query == null || query.isEmpty()) {
return Collections.emptyMap();
}
return Arrays.stream(query.split("&"))
.map(p -> splitQueryParameter(p))
.collect(groupingBy(e -> e.get0(), // group by parameter name
mapping(e -> e.get1(), toList())));// keep parameter values and assemble into list
}
public static Pair<String, Optional<String>> splitQueryParameter(String parameter) {
final String enc = "UTF-8";
List<String> keyValue = Arrays.stream(parameter.split("="))
.map(e -> {
try {
return URLDecoder.decode(e, enc);
} catch (UnsupportedEncodingException ex) {
throw new RuntimeUnsupportedEncodingException(ex);
}
}).collect(toList());
if (keyValue.size() == 2) {
return new Pair(keyValue.get(0), Optional.of(keyValue.get(1)));
} else {
return new Pair(keyValue.get(0), Optional.empty());
}
}
/** Runtime exception (instead of checked exception) to denote unsupported enconding */
public static class RuntimeUnsupportedEncodingException extends RuntimeException {
public RuntimeUnsupportedEncodingException(Throwable cause) {
super(cause);
}
}
/**
* A simple pair of two elements
* #param <U> first element
* #param <V> second element
*/
public static class Pair<U, V> {
U a;
V b;
public Pair(U u, V v) {
this.a = u;
this.b = v;
}
public U get0() {
return a;
}
public V get1() {
return b;
}
}
}
Scala Code
... and for the sake of completeness I can not resist to provide the solution in Scala that dominates by brevity and beauty
import java.net.URLDecoder
object Decode {
def main(args: Array[String]): Unit = {
val input = "a=1&b=2&c=&a=4";
println(separate(input))
}
def separate(input: String) : Map[String, List[Option[String]]] = {
case class Parameter(key: String, value: Option[String])
def separateParameter(parameter: String) : Parameter =
parameter.split("=")
.map(e => URLDecoder.decode(e, "UTF-8")) match {
case Array(key, value) => Parameter(key, Some(value))
case Array(key) => Parameter(key, None)
}
input.split("&").toList
.map(p => separateParameter(p))
.groupBy(p => p.key)
.mapValues(vs => vs.map(p => p.value))
}
}
Using above mentioned comments and solutions, I am storing all the query parameters using Map<String, Object> where Objects either can be string or Set<String>. The solution is given below. It is recommended to use some kind of url validator to validate the url first and then call convertQueryStringToMap method.
private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
public static Map<String, Object> convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException {
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME);
Map<String, Object> queryStringMap = new HashMap<>();
for(NameValuePair param : params){
queryStringMap.put(param.getName(), handleMultiValuedQueryParam(queryStringMap, param.getName(), param.getValue()));
}
return queryStringMap;
}
private static Object handleMultiValuedQueryParam(Map responseMap, String key, String value) {
if (!responseMap.containsKey(key)) {
return value.contains(",") ? new HashSet<String>(Arrays.asList(value.split(","))) : value;
} else {
Set<String> queryValueSet = responseMap.get(key) instanceof Set ? (Set<String>) responseMap.get(key) : new HashSet<String>();
if (value.contains(",")) {
queryValueSet.addAll(Arrays.asList(value.split(",")));
} else {
queryValueSet.add(value);
}
return queryValueSet;
}
}
I had a go at a Kotlin version seeing how this is the top result in Google.
#Throws(UnsupportedEncodingException::class)
fun splitQuery(url: URL): Map<String, List<String>> {
val queryPairs = LinkedHashMap<String, ArrayList<String>>()
url.query.split("&".toRegex())
.dropLastWhile { it.isEmpty() }
.map { it.split('=') }
.map { it.getOrEmpty(0).decodeToUTF8() to it.getOrEmpty(1).decodeToUTF8() }
.forEach { (key, value) ->
if (!queryPairs.containsKey(key)) {
queryPairs[key] = arrayListOf(value)
} else {
if(!queryPairs[key]!!.contains(value)) {
queryPairs[key]!!.add(value)
}
}
}
return queryPairs
}
And the extension methods
fun List<String>.getOrEmpty(index: Int) : String {
return getOrElse(index) {""}
}
fun String.decodeToUTF8(): String {
URLDecoder.decode(this, "UTF-8")
}
Also, I would recommend regex based implementation of URLParser
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class URLParser {
private final String query;
public URLParser(String query) {
this.query = query;
}
public String get(String name) {
String regex = "(?:^|\\?|&)" + name + "=(.*?)(?:&|$)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(this.query);
if (matcher.find()) {
return matcher.group(1);
}
return "";
}
}
This class is easy to use. It just needs the URL or the query string on initialization and parses value by given key.
class Main {
public static void main(String[] args) {
URLParser parser = new URLParser("https://www.google.com/search?q=java+parse+url+params&oq=java+parse+url+params&aqs=chrome..69i57j0i10.18908j0j7&sourceid=chrome&ie=UTF-8");
System.out.println(parser.get("q")); // java+parse+url+params
System.out.println(parser.get("sourceid")); // chrome
System.out.println(parser.get("ie")); // UTF-8
}
}
Kotlin's Answer with initial reference from https://stackoverflow.com/a/51024552/3286489, but with improved version by tidying up codes and provides 2 versions of it, and use immutable collection operations
Use java.net.URI to extract the Query. Then use the below provided extension functions
Assuming you only want the last value of query i.e. page2&page3 will get {page=3}, use the below extension function
fun URI.getQueryMap(): Map<String, String> {
if (query == null) return emptyMap()
return query.split("&")
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.associateBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
Assuming you want a list of all value for the query i.e. page2&page3 will get {page=[2, 3]}
fun URI.getQueryMapList(): Map<String, List<String>> {
if (query == null) return emptyMap()
return query.split("&")
.distinct()
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.groupBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
The way to use it as below
val uri = URI("schema://host/path/?page=&page=2&page=2&page=3")
println(uri.getQueryMapList()) // Result is {page=[2, 3]}
println(uri.getQueryMap()) // Result is {page=3}
There are plenty of answers which work for your query as you've indicated when it has single parameter definitions. In some applications it may be useful to handle a few extra query parameter edge cases such as:
list of parameter values such as param1&param1=value&param1= meaning param1 is set to List.of("", "value", "")
invalid permutations such as querypath?&=&&=noparamname&.
use empty string not null in maps a= means "a" is List.of("") to match web servlet handling
This uses a Stream with filters and groupingBy to collect to Map<String, List<String>>:
public static Map<String, List<String>> getParameterValues(URL url) {
return Arrays.stream(url.getQuery().split("&"))
.map(s -> s.split("="))
// filter out empty parameter names (as in Tomcat) "?&=&&=value&":
.filter(arr -> arr.length > 0 && arr[0].length() > 0)
.collect(Collectors.groupingBy(arr -> URLDecoder.decode(arr[0], StandardCharsets.UTF_8),
// drop this line for not-name definition order Map:
LinkedHashMap::new,
Collectors.mapping(arr -> arr.length < 2 ? "" : URLDecoder.decode(arr[1], StandardCharsets.UTF_8), Collectors.toList())));
}
If you are using Spring, add an argument of type #RequestParam Map<String,String> to your controller method, and Spring will construct the map for you!
Just an update to the Java 8 version
public Map<String, List<String>> splitQuery(URL url) {
if (Strings.isNullOrEmpty(url.getQuery())) {
return Collections.emptyMap();
}
return Arrays.stream(url.getQuery().split("&"))
.map(this::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new, **Collectors**.mapping(Map.Entry::getValue, **Collectors**.toList())));
}
mapping and toList() methods have to be used with Collectors which was not mentioned in the top answer. Otherwise it would throw compilation error in IDE
Answering here because this is a popular thread. This is a clean solution in Kotlin that uses the recommended UrlQuerySanitizer api. See the official documentation. I have added a string builder to concatenate and display the params.
var myURL: String? = null
if (intent.hasExtra("my_value")) {
myURL = intent.extras.getString("my_value")
} else {
myURL = intent.dataString
}
val sanitizer = UrlQuerySanitizer(myURL)
// We don't want to manually define every expected query *key*, so we set this to true
sanitizer.allowUnregisteredParamaters = true
val parameterNamesToValues: List<UrlQuerySanitizer.ParameterValuePair> = sanitizer.parameterList
val parameterIterator: Iterator<UrlQuerySanitizer.ParameterValuePair> = parameterNamesToValues.iterator()
// Helper simply so we can display all values on screen
val stringBuilder = StringBuilder()
while (parameterIterator.hasNext()) {
val parameterValuePair: UrlQuerySanitizer.ParameterValuePair = parameterIterator.next()
val parameterName: String = parameterValuePair.mParameter
val parameterValue: String = parameterValuePair.mValue
// Append string to display all key value pairs
stringBuilder.append("Key: $parameterName\nValue: $parameterValue\n\n")
}
// Set a textView's text to display the string
val paramListString = stringBuilder.toString()
val textView: TextView = findViewById(R.id.activity_title) as TextView
textView.text = "Paramlist is \n\n$paramListString"
// to check if the url has specific keys
if (sanitizer.hasParameter("type")) {
val type = sanitizer.getValue("type")
println("sanitizer has type param $type")
}
Here is my solution with reduce and Optional:
private Optional<SimpleImmutableEntry<String, String>> splitKeyValue(String text) {
String[] v = text.split("=");
if (v.length == 1 || v.length == 2) {
String key = URLDecoder.decode(v[0], StandardCharsets.UTF_8);
String value = v.length == 2 ? URLDecoder.decode(v[1], StandardCharsets.UTF_8) : null;
return Optional.of(new SimpleImmutableEntry<String, String>(key, value));
} else
return Optional.empty();
}
private HashMap<String, String> parseQuery(URI uri) {
HashMap<String, String> params = Arrays.stream(uri.getQuery()
.split("&"))
.map(this::splitKeyValue)
.filter(Optional::isPresent)
.map(Optional::get)
.reduce(
// initial value
new HashMap<String, String>(),
// accumulator
(map, kv) -> {
map.put(kv.getKey(), kv.getValue());
return map;
},
// combiner
(a, b) -> {
a.putAll(b);
return a;
});
return params;
}
I ignore duplicate parameters (I take the last one).
I use Optional<SimpleImmutableEntry<String, String>> to ignore garbage later
The reduction start with an empty map, then populate it on each SimpleImmutableEntry
In case you ask, reduce requires this weird combiner in the last parameter, which is only used in parallel streams. Its goal is to merge two intermediate results (here HashMap).
If you happen to have cxf-core on the classpath and you know you have no repeated query params, you may want to use UrlUtils.parseQueryString.
The Eclipse Jersey REST framework supports this through UriComponent. Example:
import org.glassfish.jersey.uri.UriComponent;
String uri = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
MultivaluedMap<String, String> params = UriComponent.decodeQuery(URI.create(uri), true);
for (String key : params.keySet()) {
System.out.println(key + ": " + params.getFirst(key));
}
If just want the parameters after the URL from a String. Then the following code will work. I am just assuming the simple Url. I mean no hard and fast checking and decoding. Like in one of my test case I got the Url and I know I just need the value of the paramaters. The url was simple. No encoding decoding needed.
String location = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
String location1 = "https://stackoverflow.com?param1=value1&param2=value2&param3=value3";
String location2 = "https://stackoverflow.com?param1=value1&param2=&param3=value3&param3";
Map<String, String> paramsMap = Stream.of(location)
.filter(l -> l.indexOf("?") != -1)
.map(l -> l.substring(l.indexOf("?") + 1, l.length()))
.flatMap(q -> Pattern.compile("&").splitAsStream(q))
.map(s -> s.split("="))
.filter(a -> a.length == 2)
.collect(Collectors.toMap(
a -> a[0],
a -> a[1],
(existing, replacement) -> existing + ", " + replacement,
LinkedHashMap::new
));
System.out.println(paramsMap);
Thanks
That seems tidy to me the best way:
static Map<String, String> decomposeQueryString(String query, Charset charset) {
return Arrays.stream(query.split("&"))
.map(pair -> pair.split("=", 2))
.collect(Collectors.toMap(
pair -> URLDecoder.decode(pair[0], charset),
pair -> pair.length > 1 ? URLDecoder.decode(pair[1], charset) : null)
);
}
The prerequisite is that your query syntax does not allow repeated parameters.
The Hutool framework supports this through HttpUtil. Example:
import cn.hutool.http.HttpUtil;
String url ="https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
Map<String, List<String>> stringListMap = HttpUtil.decodeParams(url, "UTF-8");
System.out.println("decodeParams:" + stringListMap);
You will get:
decodeParams:{client_id=[SS], response_type=[code], scope=[N_FULL], access_type=[offline], redirect_uri=[http://localhost/Callback]}
A kotlin version
of the answer Answer by matthias provided
fun decomposeQueryString(query: String, charset: Charset): Map<String, String?> {
return if (query.split("?").size <= 1)
emptyMap()
else {
query.split("?")[1]
.split("&")
.map { it.split(Pattern.compile("="), 2) }
.associate {
Pair(
URLDecoder.decode(it[0], charset.name()),
if (it.size > 1) URLDecoder.decode(it[1], charset.name()) else null
)
}
}
}
This takes of the first parameter after the question mark '?' as well.
Plain Java, No Special Libraries, Nothing Fancy
// assumes you are parsing a line that looks like:
// /path/resource?key=value&parameter=value
// which you got from a request header line that looks like:
// GET /path/resource?key=value&parameter=value HTTP/1.1
public HashMap<String, String> parseQuery(String path){
if(path == null || path.isEmpty()){ //basic sanity check
return null;
}
int indexOfQ = path.indexOf("?"); //where the query string starts
if(indexOfQ == -1){return null;} //check query exists
String queryString = path.substring(indexOfQ + 1);
String[] queryStringArray = queryString.split("&");
Map<String, String> kvMap = new HashMap<>();
for(String kvString : queryStringArray){
int indexOfE = kvString.indexOf("="); //check query is formed correctly
if(indexOfE == -1 || indexOfE == 0){return null;}
String[] kvPairArray = kvString.split("=");
kvMap.put(kvPairArray[0], kvPairArray[1]);
}
return kvMap;
}
org.keycloak.common.util.UriUtils
I had to parse URIs and Query Parameters in a Keycloak extension and found this utility classes very useful:
org.keycloak.common.util.UriUtils:
static MultivaluedHashMap<String,String> decodeQueryString(String queryString)
There is also a useful method to delete one query parameter:
static String stripQueryParam(String url, String name)
And to parse the URL there is
org.keycloak.common.util.KeycloakUriBuilder:
KeycloakUriBuilder uri(String uriTemplate)
String getQuery()
and lots of other goodies.

How do i write algorithm for Map and Regex together to check inputs? [duplicate]

I've got the URI like this:
https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback
I need a collection with parsed elements:
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
To be exact, I need a Java equivalent for the C#/.NET HttpUtility.ParseQueryString method.
If you are looking for a way to achieve it without using an external library, the following code will help you.
public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
You can access the returned Map using <map>.get("client_id"), with the URL given in your question this would return "SS".
UPDATE URL-Decoding added
UPDATE As this answer is still quite popular, I made an improved version of the method above, which handles multiple parameters with the same key and parameters with no value as well.
public static Map<String, List<String>> splitQuery(URL url) throws UnsupportedEncodingException {
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
final String[] pairs = url.getQuery().split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new LinkedList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
query_pairs.get(key).add(value);
}
return query_pairs;
}
UPDATE Java8 version
public Map<String, List<String>> splitQuery(URL url) {
if (Strings.isNullOrEmpty(url.getQuery())) {
return Collections.emptyMap();
}
return Arrays.stream(url.getQuery().split("&"))
.map(this::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new, mapping(Map.Entry::getValue, toList())));
}
public SimpleImmutableEntry<String, String> splitQueryParameter(String it) {
final int idx = it.indexOf("=");
final String key = idx > 0 ? it.substring(0, idx) : it;
final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null;
return new SimpleImmutableEntry<>(
URLDecoder.decode(key, StandardCharsets.UTF_8),
URLDecoder.decode(value, StandardCharsets.UTF_8)
);
}
Running the above method with the URL
https://stackoverflow.com?param1=value1&param2=&param3=value3&param3
returns this Map:
{param1=["value1"], param2=[null], param3=["value3", null]}
org.apache.http.client.utils.URLEncodedUtils
is a well known library that can do it for you
import org.apache.hc.client5.http.utils.URLEncodedUtils
String url = "http://www.example.com/something.html?one=1&two=2&three=3&three=3a";
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), Charset.forName("UTF-8"));
for (NameValuePair param : params) {
System.out.println(param.getName() + " : " + param.getValue());
}
Outputs
one : 1
two : 2
three : 3
three : 3a
If you are using Spring Framework:
public static void main(String[] args) {
String uri = "http://my.test.com/test?param1=ab&param2=cd&param2=ef";
MultiValueMap<String, String> parameters =
UriComponentsBuilder.fromUriString(uri).build().getQueryParams();
List<String> param1 = parameters.get("param1");
List<String> param2 = parameters.get("param2");
System.out.println("param1: " + param1.get(0));
System.out.println("param2: " + param2.get(0) + "," + param2.get(1));
}
You will get:
param1: ab
param2: cd,ef
use google Guava and do it in 2 lines:
import java.util.Map;
import com.google.common.base.Splitter;
public class Parser {
public static void main(String... args) {
String uri = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
String query = uri.split("\\?")[1];
final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(query);
System.out.println(map);
}
}
which gives you
{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
The shortest way I've found is this one:
MultiValueMap<String, String> queryParams =
UriComponentsBuilder.fromUriString(url).build().getQueryParams();
UPDATE: UriComponentsBuilder comes from Spring. Here the link.
For Android, if you are using OkHttp in your project. You might get a look at this. It simple and helpful.
final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
final String target = url.queryParameter("target");
final String id = url.queryParameter("id");
}
PLAIN Java 11
Given the URL to analyse:
URL url = new URL("https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback");
This solution collects a list of pairs:
List<Map.Entry<String, String>> list = Pattern.compile("&")
.splitAsStream(url.getQuery())
.map(s -> Arrays.copyOf(s.split("=", 2), 2))
.map(o -> Map.entry(decode(o[0]), decode(o[1])))
.collect(Collectors.toList());
This solution on the other hand collects a map (given that in a url there can be more parameters with same name but different values).
Map<String, List<String>> list = Pattern.compile("&")
.splitAsStream(url.getQuery())
.map(s -> Arrays.copyOf(s.split("=", 2), 2))
.collect(groupingBy(s -> decode(s[0]), mapping(s -> decode(s[1]), toList())));
Both the solutions must use an utility function to properly decode the parameters.
private static String decode(final String encoded) {
return Optional.ofNullable(encoded)
.map(e -> URLDecoder.decode(e, StandardCharsets.UTF_8))
.orElse(null);
}
On Android, there is a Uri class in package android.net . Note that Uri is part of android.net, whereas URI is part of java.net .
Uri class has many functions to extract key-value pairs from a query.
Following function returns key-value pairs in the form of HashMap.
In Java:
Map<String, String> getQueryKeyValueMap(Uri uri){
HashMap<String, String> keyValueMap = new HashMap();
String key;
String value;
Set<String> keyNamesList = uri.getQueryParameterNames();
Iterator iterator = keyNamesList.iterator();
while (iterator.hasNext()){
key = (String) iterator.next();
value = uri.getQueryParameter(key);
keyValueMap.put(key, value);
}
return keyValueMap;
}
In Kotlin:
fun getQueryKeyValueMap(uri: Uri): HashMap<String, String> {
val keyValueMap = HashMap<String, String>()
var key: String
var value: String
val keyNamesList = uri.queryParameterNames
val iterator = keyNamesList.iterator()
while (iterator.hasNext()) {
key = iterator.next() as String
value = uri.getQueryParameter(key) as String
keyValueMap.put(key, value)
}
return keyValueMap
}
If you are using servlet doGet try this
request.getParameterMap()
Returns a java.util.Map of the parameters of this request.
Returns:
an immutable java.util.Map containing parameter names as keys and parameter values as map values. The keys in the parameter map are of type String. The values in the parameter map are of type String array.
(Java doc)
Netty also provides a nice query string parser called QueryStringDecoder.
In one line of code, it can parse the URL in the question.
I like because it doesn't require catching or throwing java.net.MalformedURLException.
In one line:
Map<String, List<String>> parameters = new QueryStringDecoder(url).parameters();
See javadocs here: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html
Here is a short, self contained, correct example:
import io.netty.handler.codec.http.QueryStringDecoder;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
public class UrlParse {
public static void main(String... args) {
String url = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
QueryStringDecoder decoder = new QueryStringDecoder(url);
Map<String, List<String>> parameters = decoder.parameters();
print(parameters);
}
private static void print(final Map<String, List<String>> parameters) {
System.out.println("NAME VALUE");
System.out.println("------------------------");
parameters.forEach((key, values) ->
values.forEach(val ->
System.out.println(StringUtils.rightPad(key, 19) + val)));
}
}
which generates
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
If you're using Java 8 and you're willing to write a few reusable methods, you can do it in one line.
private Map<String, List<String>> parse(final String query) {
return Arrays.asList(query.split("&")).stream().map(p -> p.split("=")).collect(Collectors.toMap(s -> decode(index(s, 0)), s -> Arrays.asList(decode(index(s, 1))), this::mergeLists));
}
private <T> List<T> mergeLists(final List<T> l1, final List<T> l2) {
List<T> list = new ArrayList<>();
list.addAll(l1);
list.addAll(l2);
return list;
}
private static <T> T index(final T[] array, final int index) {
return index >= array.length ? null : array[index];
}
private static String decode(final String encoded) {
try {
return encoded == null ? null : URLDecoder.decode(encoded, "UTF-8");
} catch(final UnsupportedEncodingException e) {
throw new RuntimeException("Impossible: UTF-8 is a required encoding", e);
}
}
But that's a pretty brutal line.
There a new version of Apache HTTP client - org.apache.httpcomponents.client5 - where URLEncodedUtils is now deprecated. URIBuilder should be used instead:
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.net.URIBuilder;
private static Map<String, String> getQueryParameters(final String url) throws URISyntaxException {
return new URIBuilder(new URI(url), StandardCharsets.UTF_8).getQueryParams()
.stream()
.collect(Collectors.toMap(NameValuePair::getName,
nameValuePair -> URLDecoder.decode(nameValuePair.getValue(), StandardCharsets.UTF_8)));
}
A ready-to-use solution for decoding of URI query part (incl. decoding and multi parameter values)
Comments
I wasn't happy with the code provided by #Pr0gr4mm3r in https://stackoverflow.com/a/13592567/1211082 . The Stream-based solution does not do URLDecoding, the mutable version clumpsy.
Thus I elaborated a solution that
Can decompose a URI query part into a Map<String, List<Optional<String>>>
Can handle multiple values for the same parameter name
Can represent parameters without a value properly (Optional.empty() instead of null)
Decodes parameter names and values correctly via URLdecode
Is based on Java 8 Streams
Is directly usable (see code including imports below)
Allows for proper error handling (here via turning a checked exception UnsupportedEncodingExceptioninto a runtime exception RuntimeUnsupportedEncodingException that allows interplay with stream. (Wrapping regular function into functions throwing checked exceptions is a pain. And Scala Try is not available in the Java language default.)
Java Code
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import static java.util.stream.Collectors.*;
public class URIParameterDecode {
/**
* Decode parameters in query part of a URI into a map from parameter name to its parameter values.
* For parameters that occur multiple times each value is collected.
* Proper decoding of the parameters is performed.
*
* Example
* <pre>a=1&b=2&c=&a=4</pre>
* is converted into
* <pre>{a=[Optional[1], Optional[4]], b=[Optional[2]], c=[Optional.empty]}</pre>
* #param query the query part of an URI
* #return map of parameters names into a list of their values.
*
*/
public static Map<String, List<Optional<String>>> splitQuery(String query) {
if (query == null || query.isEmpty()) {
return Collections.emptyMap();
}
return Arrays.stream(query.split("&"))
.map(p -> splitQueryParameter(p))
.collect(groupingBy(e -> e.get0(), // group by parameter name
mapping(e -> e.get1(), toList())));// keep parameter values and assemble into list
}
public static Pair<String, Optional<String>> splitQueryParameter(String parameter) {
final String enc = "UTF-8";
List<String> keyValue = Arrays.stream(parameter.split("="))
.map(e -> {
try {
return URLDecoder.decode(e, enc);
} catch (UnsupportedEncodingException ex) {
throw new RuntimeUnsupportedEncodingException(ex);
}
}).collect(toList());
if (keyValue.size() == 2) {
return new Pair(keyValue.get(0), Optional.of(keyValue.get(1)));
} else {
return new Pair(keyValue.get(0), Optional.empty());
}
}
/** Runtime exception (instead of checked exception) to denote unsupported enconding */
public static class RuntimeUnsupportedEncodingException extends RuntimeException {
public RuntimeUnsupportedEncodingException(Throwable cause) {
super(cause);
}
}
/**
* A simple pair of two elements
* #param <U> first element
* #param <V> second element
*/
public static class Pair<U, V> {
U a;
V b;
public Pair(U u, V v) {
this.a = u;
this.b = v;
}
public U get0() {
return a;
}
public V get1() {
return b;
}
}
}
Scala Code
... and for the sake of completeness I can not resist to provide the solution in Scala that dominates by brevity and beauty
import java.net.URLDecoder
object Decode {
def main(args: Array[String]): Unit = {
val input = "a=1&b=2&c=&a=4";
println(separate(input))
}
def separate(input: String) : Map[String, List[Option[String]]] = {
case class Parameter(key: String, value: Option[String])
def separateParameter(parameter: String) : Parameter =
parameter.split("=")
.map(e => URLDecoder.decode(e, "UTF-8")) match {
case Array(key, value) => Parameter(key, Some(value))
case Array(key) => Parameter(key, None)
}
input.split("&").toList
.map(p => separateParameter(p))
.groupBy(p => p.key)
.mapValues(vs => vs.map(p => p.value))
}
}
Using above mentioned comments and solutions, I am storing all the query parameters using Map<String, Object> where Objects either can be string or Set<String>. The solution is given below. It is recommended to use some kind of url validator to validate the url first and then call convertQueryStringToMap method.
private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
public static Map<String, Object> convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException {
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME);
Map<String, Object> queryStringMap = new HashMap<>();
for(NameValuePair param : params){
queryStringMap.put(param.getName(), handleMultiValuedQueryParam(queryStringMap, param.getName(), param.getValue()));
}
return queryStringMap;
}
private static Object handleMultiValuedQueryParam(Map responseMap, String key, String value) {
if (!responseMap.containsKey(key)) {
return value.contains(",") ? new HashSet<String>(Arrays.asList(value.split(","))) : value;
} else {
Set<String> queryValueSet = responseMap.get(key) instanceof Set ? (Set<String>) responseMap.get(key) : new HashSet<String>();
if (value.contains(",")) {
queryValueSet.addAll(Arrays.asList(value.split(",")));
} else {
queryValueSet.add(value);
}
return queryValueSet;
}
}
I had a go at a Kotlin version seeing how this is the top result in Google.
#Throws(UnsupportedEncodingException::class)
fun splitQuery(url: URL): Map<String, List<String>> {
val queryPairs = LinkedHashMap<String, ArrayList<String>>()
url.query.split("&".toRegex())
.dropLastWhile { it.isEmpty() }
.map { it.split('=') }
.map { it.getOrEmpty(0).decodeToUTF8() to it.getOrEmpty(1).decodeToUTF8() }
.forEach { (key, value) ->
if (!queryPairs.containsKey(key)) {
queryPairs[key] = arrayListOf(value)
} else {
if(!queryPairs[key]!!.contains(value)) {
queryPairs[key]!!.add(value)
}
}
}
return queryPairs
}
And the extension methods
fun List<String>.getOrEmpty(index: Int) : String {
return getOrElse(index) {""}
}
fun String.decodeToUTF8(): String {
URLDecoder.decode(this, "UTF-8")
}
Also, I would recommend regex based implementation of URLParser
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class URLParser {
private final String query;
public URLParser(String query) {
this.query = query;
}
public String get(String name) {
String regex = "(?:^|\\?|&)" + name + "=(.*?)(?:&|$)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(this.query);
if (matcher.find()) {
return matcher.group(1);
}
return "";
}
}
This class is easy to use. It just needs the URL or the query string on initialization and parses value by given key.
class Main {
public static void main(String[] args) {
URLParser parser = new URLParser("https://www.google.com/search?q=java+parse+url+params&oq=java+parse+url+params&aqs=chrome..69i57j0i10.18908j0j7&sourceid=chrome&ie=UTF-8");
System.out.println(parser.get("q")); // java+parse+url+params
System.out.println(parser.get("sourceid")); // chrome
System.out.println(parser.get("ie")); // UTF-8
}
}
Kotlin's Answer with initial reference from https://stackoverflow.com/a/51024552/3286489, but with improved version by tidying up codes and provides 2 versions of it, and use immutable collection operations
Use java.net.URI to extract the Query. Then use the below provided extension functions
Assuming you only want the last value of query i.e. page2&page3 will get {page=3}, use the below extension function
fun URI.getQueryMap(): Map<String, String> {
if (query == null) return emptyMap()
return query.split("&")
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.associateBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
Assuming you want a list of all value for the query i.e. page2&page3 will get {page=[2, 3]}
fun URI.getQueryMapList(): Map<String, List<String>> {
if (query == null) return emptyMap()
return query.split("&")
.distinct()
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.groupBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
The way to use it as below
val uri = URI("schema://host/path/?page=&page=2&page=2&page=3")
println(uri.getQueryMapList()) // Result is {page=[2, 3]}
println(uri.getQueryMap()) // Result is {page=3}
There are plenty of answers which work for your query as you've indicated when it has single parameter definitions. In some applications it may be useful to handle a few extra query parameter edge cases such as:
list of parameter values such as param1&param1=value&param1= meaning param1 is set to List.of("", "value", "")
invalid permutations such as querypath?&=&&=noparamname&.
use empty string not null in maps a= means "a" is List.of("") to match web servlet handling
This uses a Stream with filters and groupingBy to collect to Map<String, List<String>>:
public static Map<String, List<String>> getParameterValues(URL url) {
return Arrays.stream(url.getQuery().split("&"))
.map(s -> s.split("="))
// filter out empty parameter names (as in Tomcat) "?&=&&=value&":
.filter(arr -> arr.length > 0 && arr[0].length() > 0)
.collect(Collectors.groupingBy(arr -> URLDecoder.decode(arr[0], StandardCharsets.UTF_8),
// drop this line for not-name definition order Map:
LinkedHashMap::new,
Collectors.mapping(arr -> arr.length < 2 ? "" : URLDecoder.decode(arr[1], StandardCharsets.UTF_8), Collectors.toList())));
}
If you are using Spring, add an argument of type #RequestParam Map<String,String> to your controller method, and Spring will construct the map for you!
Just an update to the Java 8 version
public Map<String, List<String>> splitQuery(URL url) {
if (Strings.isNullOrEmpty(url.getQuery())) {
return Collections.emptyMap();
}
return Arrays.stream(url.getQuery().split("&"))
.map(this::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new, **Collectors**.mapping(Map.Entry::getValue, **Collectors**.toList())));
}
mapping and toList() methods have to be used with Collectors which was not mentioned in the top answer. Otherwise it would throw compilation error in IDE
Answering here because this is a popular thread. This is a clean solution in Kotlin that uses the recommended UrlQuerySanitizer api. See the official documentation. I have added a string builder to concatenate and display the params.
var myURL: String? = null
if (intent.hasExtra("my_value")) {
myURL = intent.extras.getString("my_value")
} else {
myURL = intent.dataString
}
val sanitizer = UrlQuerySanitizer(myURL)
// We don't want to manually define every expected query *key*, so we set this to true
sanitizer.allowUnregisteredParamaters = true
val parameterNamesToValues: List<UrlQuerySanitizer.ParameterValuePair> = sanitizer.parameterList
val parameterIterator: Iterator<UrlQuerySanitizer.ParameterValuePair> = parameterNamesToValues.iterator()
// Helper simply so we can display all values on screen
val stringBuilder = StringBuilder()
while (parameterIterator.hasNext()) {
val parameterValuePair: UrlQuerySanitizer.ParameterValuePair = parameterIterator.next()
val parameterName: String = parameterValuePair.mParameter
val parameterValue: String = parameterValuePair.mValue
// Append string to display all key value pairs
stringBuilder.append("Key: $parameterName\nValue: $parameterValue\n\n")
}
// Set a textView's text to display the string
val paramListString = stringBuilder.toString()
val textView: TextView = findViewById(R.id.activity_title) as TextView
textView.text = "Paramlist is \n\n$paramListString"
// to check if the url has specific keys
if (sanitizer.hasParameter("type")) {
val type = sanitizer.getValue("type")
println("sanitizer has type param $type")
}
Here is my solution with reduce and Optional:
private Optional<SimpleImmutableEntry<String, String>> splitKeyValue(String text) {
String[] v = text.split("=");
if (v.length == 1 || v.length == 2) {
String key = URLDecoder.decode(v[0], StandardCharsets.UTF_8);
String value = v.length == 2 ? URLDecoder.decode(v[1], StandardCharsets.UTF_8) : null;
return Optional.of(new SimpleImmutableEntry<String, String>(key, value));
} else
return Optional.empty();
}
private HashMap<String, String> parseQuery(URI uri) {
HashMap<String, String> params = Arrays.stream(uri.getQuery()
.split("&"))
.map(this::splitKeyValue)
.filter(Optional::isPresent)
.map(Optional::get)
.reduce(
// initial value
new HashMap<String, String>(),
// accumulator
(map, kv) -> {
map.put(kv.getKey(), kv.getValue());
return map;
},
// combiner
(a, b) -> {
a.putAll(b);
return a;
});
return params;
}
I ignore duplicate parameters (I take the last one).
I use Optional<SimpleImmutableEntry<String, String>> to ignore garbage later
The reduction start with an empty map, then populate it on each SimpleImmutableEntry
In case you ask, reduce requires this weird combiner in the last parameter, which is only used in parallel streams. Its goal is to merge two intermediate results (here HashMap).
If you happen to have cxf-core on the classpath and you know you have no repeated query params, you may want to use UrlUtils.parseQueryString.
The Eclipse Jersey REST framework supports this through UriComponent. Example:
import org.glassfish.jersey.uri.UriComponent;
String uri = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
MultivaluedMap<String, String> params = UriComponent.decodeQuery(URI.create(uri), true);
for (String key : params.keySet()) {
System.out.println(key + ": " + params.getFirst(key));
}
If just want the parameters after the URL from a String. Then the following code will work. I am just assuming the simple Url. I mean no hard and fast checking and decoding. Like in one of my test case I got the Url and I know I just need the value of the paramaters. The url was simple. No encoding decoding needed.
String location = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
String location1 = "https://stackoverflow.com?param1=value1&param2=value2&param3=value3";
String location2 = "https://stackoverflow.com?param1=value1&param2=&param3=value3&param3";
Map<String, String> paramsMap = Stream.of(location)
.filter(l -> l.indexOf("?") != -1)
.map(l -> l.substring(l.indexOf("?") + 1, l.length()))
.flatMap(q -> Pattern.compile("&").splitAsStream(q))
.map(s -> s.split("="))
.filter(a -> a.length == 2)
.collect(Collectors.toMap(
a -> a[0],
a -> a[1],
(existing, replacement) -> existing + ", " + replacement,
LinkedHashMap::new
));
System.out.println(paramsMap);
Thanks
That seems tidy to me the best way:
static Map<String, String> decomposeQueryString(String query, Charset charset) {
return Arrays.stream(query.split("&"))
.map(pair -> pair.split("=", 2))
.collect(Collectors.toMap(
pair -> URLDecoder.decode(pair[0], charset),
pair -> pair.length > 1 ? URLDecoder.decode(pair[1], charset) : null)
);
}
The prerequisite is that your query syntax does not allow repeated parameters.
The Hutool framework supports this through HttpUtil. Example:
import cn.hutool.http.HttpUtil;
String url ="https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
Map<String, List<String>> stringListMap = HttpUtil.decodeParams(url, "UTF-8");
System.out.println("decodeParams:" + stringListMap);
You will get:
decodeParams:{client_id=[SS], response_type=[code], scope=[N_FULL], access_type=[offline], redirect_uri=[http://localhost/Callback]}
A kotlin version
of the answer Answer by matthias provided
fun decomposeQueryString(query: String, charset: Charset): Map<String, String?> {
return if (query.split("?").size <= 1)
emptyMap()
else {
query.split("?")[1]
.split("&")
.map { it.split(Pattern.compile("="), 2) }
.associate {
Pair(
URLDecoder.decode(it[0], charset.name()),
if (it.size > 1) URLDecoder.decode(it[1], charset.name()) else null
)
}
}
}
This takes of the first parameter after the question mark '?' as well.
Plain Java, No Special Libraries, Nothing Fancy
// assumes you are parsing a line that looks like:
// /path/resource?key=value&parameter=value
// which you got from a request header line that looks like:
// GET /path/resource?key=value&parameter=value HTTP/1.1
public HashMap<String, String> parseQuery(String path){
if(path == null || path.isEmpty()){ //basic sanity check
return null;
}
int indexOfQ = path.indexOf("?"); //where the query string starts
if(indexOfQ == -1){return null;} //check query exists
String queryString = path.substring(indexOfQ + 1);
String[] queryStringArray = queryString.split("&");
Map<String, String> kvMap = new HashMap<>();
for(String kvString : queryStringArray){
int indexOfE = kvString.indexOf("="); //check query is formed correctly
if(indexOfE == -1 || indexOfE == 0){return null;}
String[] kvPairArray = kvString.split("=");
kvMap.put(kvPairArray[0], kvPairArray[1]);
}
return kvMap;
}
org.keycloak.common.util.UriUtils
I had to parse URIs and Query Parameters in a Keycloak extension and found this utility classes very useful:
org.keycloak.common.util.UriUtils:
static MultivaluedHashMap<String,String> decodeQueryString(String queryString)
There is also a useful method to delete one query parameter:
static String stripQueryParam(String url, String name)
And to parse the URL there is
org.keycloak.common.util.KeycloakUriBuilder:
KeycloakUriBuilder uri(String uriTemplate)
String getQuery()
and lots of other goodies.

How to read dataframe in rdd operations

Scenario
I have two string lists containing text file path, List a, List b.
I want to to cartesian product of list a,b to achieve a cartesian dataframe comparison.
The way I am trying is first do cartesian product,
transfer it to pairRdd and then on foreach apply operation.
List<String> a = Lists.newList("/data/1.text",/data/2.text","/data/3.text");
List<String> b = Lists.newList("/data/4.text",/data/5.text","/data/6.text");
JavaSparkContext jsc = new JavaSparkContext(spark.sparkContext());
List<Tuple2<String,String>> cartesian = cartesian(a,b);
jsc.parallelizePairs(cartesian).filter(new Function<Tuple2<String, String>, Boolean>() {
#Override public Boolean call(Tuple2<String, String> tup) throws Exception {
Dataset<Row> text1 = spark.read().text(tup._1); <-- this throw NullPointerException
Dataset<Row> text2 = spark.read().text(tup._2);
return text1.first()==text2.first(); <-- this is an indicative function only
});
Even I can use spark to do cartesian as
JavaRDD<Column> sourceRdd = jsc.parallelize(a);
JavaRDD<Column> allRdd = jsc.parallelize(b);
sourceRdd.cache().cartesian(allRdd).filter(new Function<Tuple2<String, String>, Boolean>() {
#Override public Boolean call(Tuple2<Column, Column> tup) throws Exception {
Dataset<Row> text1 = spark.read().text(tup._1); <-- same issue
Dataset<Row> text2 = spark.read().text(tup._2);
return text1.first()==text2.first();
}
});
Please suggest good approach to handle this.
Not sure if I completely understood your problem. Here is the sample for Cartesian using Spark and Java.
public class CartesianDemo {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("CartesianDemo").setMaster("local");
JavaSparkContext jsc = new JavaSparkContext(conf);
//list
List<String> listOne = Arrays.asList("one", "two", "three", "four", "five");
List<String> listTwo = Arrays.asList("ww", "xx", "yy", "zz");
//RDD
JavaRDD<String> rddOne = jsc.parallelize(listOne);
JavaRDD<String> rddTwo = jsc.parallelize(listTwo);
//Cartesian
JavaPairRDD<String, String> cartesianRDD = rddOne.cartesian(rddTwo);
//print
cartesianRDD.foreach(data -> {
System.out.println("X=" + data._1() + " Y=" + data._2());
});
//stop
jsc.stop();
jsc.close();
}
}

Categories

Resources