How test URI for equals without parameter order? - java

Consider a code:
URI one = URI.create("http://localhost:8081/contextRoot/main.html?one=1&second=2");
URI second = URI.create("http://localhost:8081/contextRoot/main.html?second=2&one=1");
System.out.println(one.equals(second));
And it prints false. Is there a way to test URI without URI parameters order?

Unforunately, equals methods of URI/URL objects are not always do, what you are exactly waiting for. That is why, to compare 2 URI with different parameters order (if you think, the order is not important for you), you should use some utility logic. For example, as follows:
public static void main(String... args) {
URI one = URI.create("http://localhost:8081/contextRoot/main.html?one=1&second=2");
URI second = URI.create("http://localhost:8081/contextRoot/main.html?second=2&one=1");
System.out.println(one.equals(second));
System.out.println(areEquals(one, second));
}
private static boolean areEquals(URI url1, URI url2) {
//compare the commons part of URI
if (!url1.getScheme().equals(url1.getScheme()) ||
!url1.getAuthority().equals(url2.getAuthority()) ||
url1.getPort() != url2.getPort() ||
!url1.getHost().equals(url2.getHost())) {
return false;
}
//extract query parameters
String params1 = url1.getQuery();
String params2 = url2.getQuery();
if ((params1 != null && params2 != null) && (params1.length() == params2.length())) {
//get sorted list of parameters
List<String> list1 = extractParameters(params1);
List<String> list2 = extractParameters(params2);
//since list are sorted and contain String objects,
//we can compare the lists objects
return list1.equals(list2);
} else {
return false;
}
}
//return sorted list of parameters with the values
private static List<String> extractParameters(String paramsString) {
List<String> parameters = new ArrayList<>();
String[] paramAr = paramsString.split("&");
for (String parameter : paramAr) {
parameters.add(parameter);
}
Collections.sort(parameters);
return parameters;
}

Related

Clone an ArrayList to another ArrayList does not work in DP

Here is the Java code to find the shortest concatenation of elements of Array wordBank to construct String Terget, using Dynamic Programming.
Example:
Input: wordBank = {"ab", "c", "d", "abc", "ad"},
Target = "abcd".
Output: {"abc", "d"}.
To do this, I have stored the combination of elements as an ArrayList in a HashMap.
However, the hashMap does not store the values correctly, i.e., the values change when I recursively call the function, although I have cloned the ArrayList before adding it to the map.
Any idea why this happens?
The code works well with arrays.
static ArrayList<String> bestConstruct(String target, String[] wordBank, HashMap<String, ArrayList<String>> map) {
if(target.isEmpty())
{
return new ArrayList<String>();
}
if(map.containsKey(target))
return map.get(target);
ArrayList<String> shortestCombination = null;
for (String word : wordBank) {
if(target.startsWith(word)) {
String newTarget = target.substring(word.length(), target.length());
ArrayList<String> combination = bestConstruct(newTarget, wordBank, map);
if(combination != null) {
combination.add(word);
if(shortestCombination == null || combination.size() < shortestCombination.size())
shortestCombination = (ArrayList<String>)(combination.clone());
}
}
}
map.put(target, (ArrayList<String>) (shortestCombination.clone()));
return shortestCombination;
}
The problem is the interaction between these lines:
if(map.containsKey(target))
return map.get(target);
and
ArrayList<String> combination = bestConstruct(newTarget, wordBank, map);
if(combination != null) {
combination.add(word);
If you return the memoized list, you're updating it before you clone it.
In general, don't rely on callers to "do the right thing": if you don't want the list in the map to be updated, do the copy yourself before you return it:
if(map.containsKey(target))
return new ArrayList<>(map.get(target));
You may also need to handle the case of a string not being able to be constructed from the word bank.

Java 8 Streams - Compare two Lists' object values and add value to new List?

I have two Lists containing objects of this class:
public class SchoolObj
{
private String name;
private String school;
public SchoolObj()
{
this(null, null);
}
public SchoolObj(String nameStr, String schoolStr)
{
this.setName(nameStr);
this.setSchool(schoolStr);
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
public String getSchool()
{
return this.school;
}
public void setSchool(String school)
{
this.school = school;
}
#Override
public String toString()
{
return this.getName() + ' ' + this.getSchool();
}
}
I want to compare the objects in those two lists by name and school. If they are equal I need to create a new List containing those SchoolObj objects which are found in both Lists.
I know we can use two for loops and do it is in the createSharedListViaLoop method below.
My question is, how can I accomplish the same thing with Java streams?
I tried with createSharedListViaStream below, but it is not working as expected.
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamTest
{
public static void main(String[] args)
{
List<SchoolObj> listOne = new ArrayList<SchoolObj>();
// TODO: Add sample data to listOne.
listOne.add(new SchoolObj("nameA", "schoolX"));
listOne.add(new SchoolObj("nameC", "schoolZ"));
List<SchoolObj> listTwo = new ArrayList<SchoolObj>();
// TODO: Add sample data to listTwo.
listTwo.add(new SchoolObj("nameA", "schoolX"));
listTwo.add(new SchoolObj("nameB", "schoolY"));
// Print results from loop method.
System.out.println("Results from loop method:");
List<SchoolObj> resultsViaLoop = StreamTest.createSharedListViaLoop(listOne, listTwo);
for (SchoolObj obj : resultsViaLoop)
{
System.out.println(obj);
}
// Print results from stream method.
System.out.println("Results from stream method:");
List<SchoolObj> resultsViaStream = StreamTest.createSharedListViaStream(listOne, listTwo);
for (SchoolObj obj : resultsViaStream)
{
System.out.println(obj);
}
}
public static List<SchoolObj> createSharedListViaLoop(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
List<SchoolObj> result = new ArrayList<SchoolObj>();
for (SchoolObj one : listOne)
{
for (SchoolObj two : listTwo)
{
if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
{
result.add(one);
}
}
}
return result;
}
public static List<SchoolObj> createSharedListViaStream(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
List<SchoolObj> listOneList = listOne.stream().filter(two -> listTwo.stream()
.anyMatch(one -> one.getName().equals(two.getName()) && two.getSchool().equals(one.getSchool())))
.collect(Collectors.toList());
return listOneList;
}
}
Let's run through each part of the code. First, createSharedListViaStream:
public static List<SchoolObj> createSharedListViaStream(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
// We create a stream of elements from the first list.
List<SchoolObj> listOneList = listOne.stream()
// We select any elements such that in the stream of elements from the second list
.filter(two -> listTwo.stream()
// there is an element that has the same name and school as this element,
.anyMatch(one -> one.getName().equals(two.getName())
&& two.getSchool().equals(one.getSchool())))
// and collect all matching elements from the first list into a new list.
.collect(Collectors.toList());
// We return the collected list.
return listOneList;
}
After running through the code, it does exactly what you want it to do. Now, let's run through createSharedListViaLoop:
public static List<SchoolObj> createSharedListViaLoop(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
// We build up a result by...
List<SchoolObj> result = new ArrayList<SchoolObj>();
// going through each element in the first list,
for (SchoolObj one : listOne)
{
// going through each element in the second list,
for (SchoolObj two : listTwo)
{
// and collecting the first list's element if it matches the second list's element.
if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
{
result.add(one);
}
}
}
// We return the collected list
return result;
}
So far, so good... right? In fact, your code in createSharedListViaStream is fundamentally correct; instead, it is your createSharedListViaLoop that may be causing discrepancies in output.
Think about the following set of inputs:
List1 = [SchoolObj("nameA","SchoolX"), SchoolObj("nameC","SchoolZ")]
List2 = [SchoolObj("nameA","SchoolX"), SchoolObj("nameA","SchoolX"), SchoolObj("nameB","SchoolY")]
Here, createSharedListViaStream will return the only element of the first list that appears in both lists: SchoolObj("nameA","SchoolX"). However, createSharedListViaLoop will return the following list: [SchoolObj("nameA","SchoolX"),SchoolObj("nameA","SchoolX")]. More precisely, createSharedListViaLoop will collect the correct object, but it will do so twice. I suspect this to be the reason for the output of createSharedListViaStream to be "incorrect" based on comparison to the output of createSharedListViaLoop.
The reason that createSharedListViaLoop does this duplication is based on the lack of termination of its inner for loop. Although we iterate over all elements of the first list to check if they are present in the second, finding a single match will suffice to add the element to the result. We can avoid redundant element addition by changing the inner loop to the following:
for (SchoolObj one : listOne)
{
for (SchoolObj two : listTwo)
{
if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
{
result.add(one);
break;
}
}
}
Additionally, if you don't want duplicate Objects in your list (by location in memory), you can use distinct like so:
List<SchoolObj> result = ...;
result = result.stream().distinct().collect(Collectors.toList());
As a final caution, the above will keep the results distinct in the following scenario:
List<SchoolObj> list = new ArrayList<>();
SchoolObj duplicate = new SchoolObj("nameC", "schoolD");
listOne.add(duplicate);
listOne.add(duplicate);
list.stream().distinct().forEach(System.out::println);
// prints:
// nameC schoolD
However, it will not work in the following scenario, unless you override the equals method for SchoolObj:
List<SchoolObj> list = new ArrayList<>();
listOne.add(new SchoolObj("nameC", "schoolD"));
listOne.add(new SchoolObj("nameC", "schoolD"));
list.stream().distinct().forEach(System.out::println);
// prints (unless Object::equals overridden)
// nameC schoolD
// nameC schoolD
You can filter in one list if contains in another list then collect.
List<SchoolObj> listCommon = listTwo.stream()
.filter(e -> listOne.contains(e))
.collect(Collectors.toList());
You need to override equals() method in SchoolObj class. contains() method you will uses the equals() method to evaluate if two objects are the same.
#Override
public boolean equals(Object o) {
if (!(o instanceof SchoolObj))
return false;
SchoolObj n = (SchoolObj) o;
return n.name.equals(name) && n.school.equals(school);
}
But better solution is to use Set for one list and filter in another list to collect if contains in Set. Set#contains takes O(1) which is faster.
Set<SchoolObj> setOne = new HashSet<>(listOne);
List<SchoolObj> listCommon = listTwo.stream()
.filter(e -> setOne.contains(e))
.collect(Collectors.toList());
You need to override hashCode() method also along with equals() in SchoolObj class for Set#contains.(assuming name and school can't be null)
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + name.hashCode();
result = prime * result + school.hashCode();
return result;
}
Here you will get details how to override equals and hashCode in a better way
In my case I had Two lists. I compared both lists having common emails and collected the objects like this:
List<TUsers> comparedUsersOnEmail = tUsers.stream().filter(o1 -> userR.stream()
.anyMatch(o2->o2.getEmail().equals(o1.getEmail()))).collect(Collectors.toList());

put and get for nested hashmap in java

I am really new to Java and I am trying to implement something using Hashmap.
The following code is what I declared first:
private HashMap<String, TreeMap<Object, Object>> submissions = new HashMap<String, TreeMap<Object, Object>>();;
And,
public Submission add(String unikey, Date timestamp, Integer grade) {
// check the argument
if(unikey == null || timestamp == null || grade == null) {
throw new IllegalArgumentException("Null argument detected\n");
}
}
this is what I am writing at the moment. Assuming that there are items called "person", "data" and "grade". Can someone please tell me how to put them in the nested hashmap? I finished writing the getter and setter for each of the items in another class called, MySubmissions.
The Submission is an interface written in another class that contain the following methods:
public String getPerson();
public Date getTime();
public Integer getGrade();
What I want to achieve is that, for example,
?.add("aaaa1234", df.parse("2016/09/03 09:00:00"), 10);
?.add("aaaa1234", df.parse("2016/09/03 16:00:00"), 20);
?.add("cccc1234", df.parse("2016/09/03 16:00:00"), 30);
?.add("aaaa1234", df.parse("2016/09/03 18:00:00"), 40);
Thanks!
(what I exactly want to achieve is, I want to add data into the hashmap. And then using another method called, getBestGrade, I want to get the best graded person among the list but I just want to know how to store into the hashmap first using put and get...)
Create an entity
public class Submission {
private Date timestamp;
private Integer grade;
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public Integer getGrade() {
return grade;
}
public void setGrade(Integer grade) {
this.grade = grade;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Submission that = (Submission) o;
if (timestamp != null ? !timestamp.equals(that.timestamp) : that.timestamp != null) return false;
return grade != null ? grade.equals(that.grade) : that.grade == null;
}
#Override
public int hashCode() {
int result = timestamp != null ? timestamp.hashCode() : 0;
result = 31 * result + (grade != null ? grade.hashCode() : 0);
return result;
}
}
Create a HashMap
private HashMap<String, Submission> map = new HasMap<>();
Do add
map.add("key", new Submission());
I think he wants to know how to store more than one Submission for each Person. You can do something like this:
import java.util.Date;
import java.util.HashMap;
import java.util.TreeMap;
public final class BestGrade
{
private static final HashMap<String, TreeMap<Date, Integer>> SUBMISSIONS = new HashMap<String, TreeMap<Date, Integer>>();
private BestGrade()
{}
public static void main(final String[] args)
{
// How to add
add("Person1", new Date(), Integer.valueOf(1));
add("Person1", new Date(), Integer.valueOf(10));
add("Person1", new Date(), Integer.valueOf(20));
add("Person2", new Date(), Integer.valueOf(1));
add("Person3", new Date(), Integer.valueOf(30));
add("Person3", new Date(), Integer.valueOf(40));
// How to get best grade
final Integer bestGradePerson1 = getBestGrade("Person1");
final Integer bestGradePerson3 = getBestGrade("Person2");
final Integer bestGradePerson2 = getBestGrade("Person3");
System.out.println("Bestgrade Person1: " + bestGradePerson1);
System.out.println("Bestgrade Person2: " + bestGradePerson2);
System.out.println("Bestgrade Person3: " + bestGradePerson3);
}
public static void add(final String key, final Date timestamp, final Integer grade)
{
// TODO the same for timestamp and grade
if (key == null || key.trim().isEmpty()) {
throw new IllegalArgumentException("key must not be null");
}
// Get
TreeMap<Date, Integer> submission = SUBMISSIONS.get(key);
// Create your treemap if not already exists, before adding new value to avoid NullPointerException
if (submission == null) {
submission = new TreeMap<Date, Integer>();
SUBMISSIONS.put(key, submission);
}
submission.put(timestamp, grade);
}
public static Integer getBestGrade(final String key)
{
Integer bestGrade = null;
final TreeMap<Date, Integer> submission = SUBMISSIONS.get(key);
if (submission == null) {
// When no submission available, return null or any other value you wish to show there is no best grade
return bestGrade;
}
for (final Integer grade : submission.values()) {
if (bestGrade == null) {
bestGrade = grade;
}
// Set new grade when values is higher than before
else if (bestGrade.intValue() < grade.intValue()) {
bestGrade = grade;
}
}
return bestGrade;
}
}
I'm just going to describe how to use a map of maps -- it's up to you to decide whether this is what you actually want to use. I'm going to use classes called A,B,C etc. -- you can substitute your own, including String or Submission if you like.
Make sure you have a firm understanding of a single-level Map before you tackle this -- how equals() and hashCode() are necessary for HashMap etc.
You can define a map of maps much as you have done:
Map<A, ? extends Map<B,C>> mapOfMaps;
In general, give variables a type of Map rather than HashMap or TreeMap -- you normally don't need any of the more specific methods of the implementation classes. You can always change up if you do. The ? extends Map<> part allows your map-of-maps to contains arbitrary implementations of Map.
You can instantiate this like this:
Map<A, ? extends Map<B,C>> mapOfMaps = new HashMap<>();
// or with explicit (unnecessary) type declarations:
Map<A, ? extends Map<B,C>> mapOfMaps = new HashMap<A, ? extends Map<B,C>>();
Now you have an empty map-of-maps. You can add a map to it:
Map<B,C> map = new HashMap<>();
mapOfMaps.put(new A(1), map);
Now you have a map-of-maps containing one empty map. Or you could add a map containing something:
Map<B,C> map = new HashMap<>();
map.put(b, c);
mapOfMaps.put(a, map);
Plausibly, you want to add items to a Map<B,C> when you don't know whether it exists. There's no short-cut here - you have to do:
void addToMapOfMaps(A a, B b, C c) {
Map<B,C> map = mapOfMaps.get(a);
if(map == null) {
map = new HashMap<>();
mapOfMaps.put(a,map);
}
map.put(b,c);
}
Note that this has problems if multiple threads are doing it at the same time.
Likewise if you're just reading, you have to handle missing elements at both levels:
C get(A a, B b) {
Map<B,C> map = mapOfMaps.get(a);
if(map == null) {
return null;
}
return map.get(b);
}
(Or more compactly)
C get(A a, B b) {
Map<B,C> map = mapOfMaps.get(a);
return map == null ? null : map.get(b);
}

What am I missing in this recursive function in java

So I have this homework (I am not trying to cheat) and I posed this question on our class forum, but I am having difficulty understanding the pseudocode that the professor wrote ... how it could possibly work.
The objective is to take a list of strings and "r","w","b" ...
then reorder them into 6 new strings ...
This is java I wrote based on his pseudocode:
public class Solution2 {
public static ArrayList<String> solve(ArrayList<String> base) {
ArrayList<String> temp = new ArrayList<String>();
ArrayList<String> result = new ArrayList<String>();
// Check for the empty set
if (base == null) {
ArrayList<String> empty = new ArrayList<String>();
return empty;
}
char first = 'y';
String firstString = "";
for (Iterator<String> i = base.iterator(); i.hasNext();) {
if (first == 'y') {
firstString = i.next();
base.remove(0);
first = 'n';
}
temp = solve(base);
for (Iterator<String> n = temp.iterator(); n.hasNext();) {
// Add first string
result.add(firstString + n.next());
}
return result;
//
}
return result;
}
}
I just don't know how you keep accumulating the main list of strings. I get an empty set ... which is the base case.
I think you should back up and consider a simpler case before you try to make sense of your professor's code, so you know what to look for.
Your recursive function needs a step where it returns a list with the new element added to it and also with the result of the next recursive call.
Say we have a function where we return a list made of the separate characters of a list:
static List<String> listChars(String s) {
if ("".equals(s)) return new ArrayList<String>();
List<String> ret = new ArrayList<>();
ret.add(s.substring(0,1));
ret.addAll(listChars(s.substring(1)));
return ret;
}
The test harness looks like
public static void main(String[] args) {
System.out.println("result of '' =" + listChars(""));
System.out.println("result of 'a'=" + listChars("a"));
System.out.println("result of 'abcd'=" + listChars("abcd"));
}
which prints
result of '' =[]
result of 'a'=[a]
result of 'abcd'=[a, b, c, d]
Your function doesn't have a case like this where you're adding the result of the current iteration plus the results from the next recursive call to the list that you're returning.

How to avoid string repetition

I have two different string String A="example"; String B="example";
if concat both the string i am getting examplexample. Is there any possibility to avoid repetition of string with same name..??
How about this ?
if(!a.equals(b)){// or if needed use contains() , equalIgnoreCase() depending on your need
//concat
}
The Strings are not different, the same String object is assigned to two different variables ("two pointers to the same memory address").
Consider dumping all strings to a Set before concatenating, this avoids duplicates in the concatenated sequence:
Set<String> strings = new HashSet<String>();
StringBuilder resultBuilder = new StringBuilder();
for (String s:getAllStrings()) { // some magic to get all your strings
if (strings.contains(s))
continue; // has been added already
resultBuilder.append(s); // concatenate
strings.add(s); // put string to set
}
String result = resultBuilder.toString();
You can. Try something like this
private String concatStringExample1(String firstString, String secondString) {
if(firstString.equalsIgnoreCase(secondString)) { // String matched
return firstString; // or return secondString
} else { // Not matched
return firstString.concat(secondString);
}
}
or
private String concatStringExample2(String firstString, String secondString) {
if(firstString != null && firstString != null ) {
if(firstString.toLowerCase().indexOf(secondString.toLowerCase()) >= 0)
return firstString;
else if(secondString.toLowerCase().indexOf(firstString.toLowerCase()) >= 0)
return secondString;
else
return firstString.concat(secondString);
} else {
return "";
}
}
Just create a Set (It has mathematics set behaviour, it won't accept the duplicate objects)
Set<String> strings = new HashSet<String>();
//Fill this set with all the String objects
strings.add(A)
Strings.add(B)
//Now iterate this set and create a String Object
StringBuilder resultBuilder = new StringBuilder();
for(String string:Strings){
resultBuilder.append(string);
}
return resultBuilder.toString()
`

Categories