简体   繁体   中英

How can I test if an array contains each value from map?

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 Stream s 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.

您可以使用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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM