I have a map:
Map<String, String> abc = new HashMap<>();
"key1" : "value1",
"key2" : "value2"
And an array:
String[] options= {"value1", "value2", "value3"}
I am creating this array as following (I am using following method to do something else which is not relevant to the question that I am asking here):
public String[] getOptions() {
List<String> optionsList = getOptionsFromAMethod(WebElementA);
String[] options = new String[optionsList.size()];
options = optionsList.toArray(options);
return options;
}
What is the best way to verify if String[] contains each value from Map?
I am thinking about doing this:
for (Object value : abc.values()) {
Arrays.asList(options).contains(value);
}
Explanation
Your current approach creates an ArrayList (from java.util.Arrays, not to confuse with the regular ArrayList from java.util) wrapping the given array.
You then call, for each value of the map, the ArrayList#contains method. However this method is very slow. It walks through the whole list in order to search for something.
Your current approach thus yields O(n^2) which doesn't scale very well.
Solution
We can do better by using a data-structure which is designed for a fast contains query, namely a HashSet.
So instead of putting all your values into an ArrayList we will put them into a HashSet whose contains method is fast:
boolean doesContainAll = true;
HashSet<String> valuesFromArray = new HashSet<>(Arrays.asList(options));
for (String value : abc.values()) {
if (!valuesFromArray.contains(value)) {
doesContainAll = false;
break;
}
}
// doesContainAll now is correctly set to 'true' or 'false'
The code now works in O(n) which is far better and also optimal in terms of complexity.
Of course you can optimize further to speedup by constant factors. For example you can first check the size, if options.length is greater than abc.values().size() then you can directly return with false.
JStream solution
You can also use Java 8 and Streams to simplify the above code, the result and also the procedure behind the scenes is the same:
HashSet<String> valuesFromArray = new HashSet<>(Arrays.asList(options));
boolean doesContainAll = abc.values().stream()
.allMatch(valuesFromArray::contains);
Insights of ArrayList#contains
Let's take a closer look into java.util.Arrays.ArrayList. You can find its code here.
Here is its code for the contains method:
public boolean contains(Object o) {
return indexOf(o) != -1;
}
Lets see how indexOf is implemented:
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
So indeed, in all cases the method will traverse from left to right through the source array in order to find the object. There is no fancy method that is able to directly access the information whether the object is contained or not, it runs in O(n) and not in O(1).
Note on duplicates
If either of your data may contain duplicates and you plan to count them individually, then you will need a slightly different approach since contains will not bother for the amount of duplicates.
For this you may collect your abc.values() first into a List for example. Then, every time you checked an element, you will remove the matched element from the List.
Alternatively you can setup a HashMap<String, Integer> which counts for every element its occurrences. Then, every time you checked an element, decrease the counter by one.
You can use https://docs.oracle.com/javase/7/docs/api/java/util/List.html#containsAll(java.util.Collection)
Arrays.asList("value1", "value2", "value3").containsAll(abc.values())
I would recommend using a stream:
final List<String> optionsList = Arrays.asList(options);
abc.values().stream().allMatch(optionsList::contains);
Related
Given the following datatype Testcase (XQuery, Testpath, FirstInputFile, SecondInputFile, Expected)
how can I properly delete duplicates.
Definition of duplicates:
If FirstInputFile already in the list as SecondInputFile vice versa.
Here is the Testdata
tcs.add(new HeaderAndBodyTestcase("XQ 1", "/1", "FAIL", "FAIL2", "FAILED"));
tcs.add(new HeaderAndBodyTestcase("XQ 1", "/1", "FAIL2", "FAIL", "FAILED"));
tcs.add(new HeaderAndBodyTestcase("XQ 2", "/2", "FAIL4", "FAIL3", "FAILED2"));
tcs.add(new HeaderAndBodyTestcase("XQ 2", "/2", "FAIL3", "FAIL4", "FAILED2"));
and here is the function
protected void deleteExistingDuplicatesInArrayList(final ArrayList<HeaderAndBodyTestcase> list) {
for (int idx = 0; idx < list.size() - 1; idx++) {
if (list.get(idx).firstInputFile.equals(list.get(idx).secondInputFile)
|| (list.get(idx + 1).firstInputFile.equals(list.get(idx).firstInputFile)
&& list.get(idx).secondInputFile.equals(list.get(idx + 1).secondInputFile)
|| (list.get(idx).firstInputFile.equals(list.get(idx + 1).secondInputFile)
&& list.get(idx).secondInputFile.equals(list.get(idx + 1).firstInputFile)))) {
list.remove(idx);
}
}
}
This solution is already working, but seems very crappy, so is there a better solution to this?
put everything in a Set using a comparator if necessary, and create a list from this set if you really need a List (and not a Collection)
Set<HeaderAndBodyTestcase> set = new Hashset<>(list);
Given your rather peculiar "equality" constraints, I think the best way would be to maintain two sets of already seen first- and second input files and a loop:
Set<String> first = new HashSet<>();
Set<String> second = new HashSet<>();
for (HeaderAndBodyTestcase tc : tcs) {
if (! first.contains(tc.getSecondInputFile()) &&
! second.contains(tc.getFirstInputFile())) {
first.add(tc.getFirstInputFile());
second.add(tc.getSecondInputFile());
System.out.println(tc); // or add to result list
}
}
This will also work if "equal" elements do not appear right after each other in the original list.
Also note that removing elements from a list while iterating the same list, while working sometimes, will often yield unexpected results. Better create a new, filtered list, or if you have to remove, create an Iterator from that list and use it's remove method.
On closer inspections (yes, it took me that long to understand your code), the conditions in your current working code are in fact much different than what I understood from your question, namely:
remove element if first and second is the same (actually never checked for the last element in the list)
remove element if first is the same as first on last, and second the same as second on last
remove if first is same as last second and vice versa
only consider consecutive elements (from comments)
Given those constraints, the sets are not needed and also would not work properly considering that both the elements have to match (either 'straight' or 'crossed'). Instead you can use pretty much your code as-is, but I would still use an Iterator and keep track of the last element, and also split the different checks to make the whole code much easier to understand.
HeaderAndBodyTestcase last = null;
for (Iterator<HeaderAndBodyTestcase> iter = list.iterator(); iter.hasNext();) {
HeaderAndBodyTestcase curr = iter.next();
if (curr.firstInputFile.equals(curr.secondInputFile)) {
iter.remove();
}
if (last != null) {
boolean bothEqual = curr.firstInputFile.equals(last.firstInputFile)
&& curr.secondInputFile.equals(last.secondInputFile);
boolean crossedEqual = curr.secondInputFile.equals(last.firstInputFile)
&& curr.firstInputFile.equals(last.secondInputFile);
if (bothEqual || crossedEqual) {
iter.remove();
}
}
last = curr;
}
I have a list of objects, in that list could be duplicate elements and I need to check each element in that list with all elements the list, if I found a duplicate element I have to mark it as duplicate.
I did this with normal for loop as show :
for (int i = 0; i < records.size() - 1; i++) {
Record record = records.get(i);
for (int k = i + 1; k < records.size(); k++) {
Record currentRecord = records.get(k);
if (RecordsParser.isDuplicateRecord(record, currentRecord)) {
currentRecord.setValid(false);
currentRecord.setErrorCode(ErrorCodes.DUPLICATE_ID);
}
}
}
So my question is : Is there any way to this logic with lambda expresion in a cleanner way ?
I would suggest not using lamda for something like this because as ParkerHalo said lamda expressions are not cleaner always. Your implementation has the worst complexity which is O(n^2). If I have well understand the problem I would use the following implementation for something more efficient (O(n)) and cleaner:
for (Element e : records) {
if (set.add(e) == false) {
e.setValid(false);
e.setErrorCode(ErrorCodes.DUPLICATE_ID);
}
}
I'll try to explain my thoughts to this as clear as I can, but since I don't know what your Record class looks like, I might as well miss the topic.
As Dimitris stated, you have a complexity of O(n²) which is really bad for performance. Your goal should be to reach linear complexity O(n) or at least O(n*log(n)).
How could you achieve that?
Use a HashSet to store the elements one by one
If your hash function is good the lookup of an element will (usually) be constant O(1)
Iterating over every single element with a constant lookup will result in a total complexity of O(n)
Small example:
class Record
{
// The records are compared with these fields:
int field1;
int field2;
#Override
public int hashCode()
{
// ** You'll have to think about a good hash function for your example!**
return 31 * field1 + 17 * field2;
}
#Override
public boolean equals(Object obj)
{
// You'll have to adapt your equals method to your own record class
if (!(obj instanceof Record))
return false;
Record other = (Record) obj;
return this.field1 == other.field1 && this.field2 == other.field2;
}
}
And this is how you use it:
HashSet<Record> set = new HashSet<>();
for(Record r : records)
{
// If your hashCode function is good this will most likely be O(1)
if (set.contains(r))
{
// You found a duplicate. Handle it here accordingly.
// ...
}
else
{
// No duplicate, add it to the set. (Good hashCode --> mostly O(1) )
set.add(r);
}
}
Please note that this is only a vague example and that you'll have to adapt your hashCode and equals methods accordingly!
Map<String, Integer> successors = new HashMap <String, Integer> ();
// I have added some elements into the successors.
Collection<Integer> uniqueValues = successors.values();
Is there a way for me to find out in java if uniqueValues can show me that all the values in it are the same?
I planned on using the if(uniqueValues.contains(1))statement. But I just could not figure it out. Since this statement will say true if 1 is present and other values different from 1 are also present. I just want it to return true if 1 is the only value in the collections.
eg; {1,1,1,1,1,1,1,1,1,1,1,1,1} should return true.
But {1,2,1,3,1,4,2,4,22,1,1,1,4} should return false.
Some code along the lines of "Contains if and only if."
This will be of great help. Thanks in advance.
Do it the CPU-clock-wasting way:
if(new HashSet(successors.values()).size()>1) {...}
Well, you could do something like this (inefficiently),
boolean uniqueValues = new HashSet<Integer>(successors.values()).size() == 1;
Since that will check every value every time, a more efficient approach might be,
boolean uniqueValues = true;
Collection<Integer> values = successors.values();
Iterator<Integer> iter = values.iterator();
if (iter.hasNext()) {
int t = iter.next();
while (iter.hasNext()) {
int i = iter.next();
if (t != i) {
// stop if we find a different value
uniqueValues = false;
break;
}
}
}
Some sort of Set sounds like easiest solution
if(new HashSet<Integer>(successors.values)).size() == 1)
Because Set can contain only unique values, logical consequence of having input collection with same values is the Set of size one. Or you can of course introduce you own util method which will check this condition.
I want to get specific combination of permutation of string like alphabet. To understand me, I'll show you the code that I using:
public class PermutationExample {
public static List<String> getPermutation(String input) {
List<String> collection = null;
if (input.length() == 1) {
collection = new ArrayList<String>();
collection.add(input);
return collection;
} else {
collection = getPermutation(input.substring(1));
Character first = input.charAt(0);
List<String> result = new ArrayList<String>();
for (String str : collection) {
for (int i = 0; i < str.length(); i++) {
String item = str.substring(0, i) + first
+ str.substring(i);
result.add(item);
}
String item = str.concat(first.toString());
result.add(item);
}
return result;
}
}
public static void main(String[] args) {
System.out.println(PermutationExample.getPermutation("ABCD"));
}
}
This code works well and i can get every combination, I can take it from the list, if I need 5-th element, I can receive it. But if the string is the alphabet ... , didn't works, it's too big. What I have to do, to get the specific element like 1221-th from all 26! combinations ?
I solved a similar problem a while ago, only in python.
If what you need is simply the n-th permutation, then you can do a lot better then generating every permutation and returning the n-th, if you try to think about generating only the permutation you need.
You can do this "simply" by figuring out what should be the element in front for the number of permutations you want, and then what should be the remaining of the elements recursively.
Assume a collection of values [0, ... ,X], for any values such that col[n] < col[n+1]
For N elements, there are N! possible permutations, the case when the collection will be perfectly reversed.
We will see the change in the head of the collection after each (N-1)! permutations, so if n < (N-1)!, the head is the head. You then have a remaining number of permutations, and you can apply the same logic recursively.
Does this help? I know it's fairly high level and you'll have to think a bit about it, but maybe it'll get you on the right track.
I have a List containing HashMaps. Each HashMap in the list might have multiple key/value pairs. I want to indexOf on the list to find out the index of the element where the passed in HashMap is. However, the problem is that equals method of HashMap looks at all the entire entrySet while comparing. Which is not what I want.
Example:
List<HashMap> benefit = new ArrayList<HashMap>();
HashMap map1 = new HashMap();
map1.put("number", "1");
benefit.add(map1);
HashMap map2 = new HashMap();
map2.put("number", "2");
map2.put("somethingelse", "blahblah"); //1
benefit.add(map2);
HashMap find = new HashMap();
find.put("number", "2");
int index = benefit.indexOf(find);
if (index >= 0)
System.out.println(benefit.get(index).get("number"));
The above code does not print anything because of line with //1.
What do I have to do so that the above code actually prints 2?
Is there a way to implement comparable on the list so that I can define
my own?
I think you're looking for retainAll(), so you can compare only the elements you're interested in:
int index = myIndexOf(benefit, find);
...
static int myIndexOf(List<HashMap> benefit, Map find) {
int i = 0;
for (Map map : benefit) {
Map tmp = new HashMap(map);
tmp.keySet().retainAll(find.keySet());
if (tmp.equals(find)) {
return i;
}
i++;
}
return -1;
}
It's possible, of course, to declare your own subclass of List that overrides the indexOf method with this behaviour. However, I don't think that's a good idea. It would violate the contract of the indexOf method:
returns the lowest index i such that (o==null ? get(i)==null : o.equals(get(i)))
This would be confusing to someone else maintaining the code. You might then think that you could subclass HashMap to redefine equals, but that would violate the symmetry property of Object.equals().
The way you are trying to achieve your goal is wrong. The indexOf method works exactly as it should in this case. It is trying to find an exact match, not a partial one.
What you are trying to do, if I get it correctly, is to find a map in your list of maps that contains a specific entry. In this case, you should manually perform this search, by going through all the maps, calling containsKey (), and then comparing the value you are expecting to find with the value associated with the key.
The other way would be to create a proxy class around your List, and add a new method findMapWithEntry (String key, String value), which would perform this seach for you (the same search I described above).
Why not change the way you search?
List<Map> matchingBenefits = new ArrayList<Map>();
for (Map m : benefit) {
if (m.containsKey("number") && m.get("number").equals("2"))
matchingBenefits.add(m);
}
for (Map m : matchingBenefits) {
System.out.println(m.get("number"));
}
You can always override the indexOf method. Looking at the source for ArrayList:
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
So it's not a very complex search algorithm at all. You may look at something like:
List benefit = new ArrayList(){
public int indexOf(Object o){
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
else {
for (int i = 0; i < size; i++) //traverse the hashmaps
Object key = ((HashMap)o).keySet().get(0); //assuming one pair
Object val = ((HashMap)o).valueSet().get(0);
if (
((HashMap)elementData[i]).containsKey(key) &&
((HashMap)elementData[i]).get(key).equals(val))
return i;
}
return -1;
};
My advice would be to consider a different data structure, perhaps writing your own one for it.
Given that you cannot change the design, would writing your own find method help?
The code below should work if I understood what you're trying to do and it runs in O(n)
public static String find(List<HashMap<String,String>> listMap, String key, String value) {
for(int i = 0; i < listMap.size(); i++)
if(listMap.get(i).get(key).equals(value))
return value;
return null;
}