5

I have two string arrays keys and values

String[] keys = {a,b,c,d};

String[] values = {1,2,3,4};

What is the fastest way to convert them into a map? I know we can iterate through them. But, is there any utility present?

Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
user2434
  • 6,339
  • 18
  • 63
  • 87
  • 3
    Python is so much better at this :P (http://stackoverflow.com/questions/209840/map-two-lists-into-a-dictionary-in-python) – Jason Sperske Sep 14 '12 at 05:01
  • Hmmm... I would liked suggesting extension methods. But unfortuntaley, it is in C# only :D –  Sep 14 '12 at 05:16
  • @Benedictus oh that I could I could have only added "in my opinion", in my defense it was 4 years ago and I was rather in love with Python. I will say that it is still my opinion but I don't seriously mean that the OP or anyone else should consider switching languages just to work with arrays :) – Jason Sperske Nov 11 '16 at 12:14

5 Answers5

11

Faster than this?

Map<String,String> map = new HashMap<>();

if(keys.length == values.length){
    for(int index = 0; index < keys.length; index++){
        map.put(keys[index], values[index]);
    }
}
saw303
  • 8,051
  • 7
  • 50
  • 90
Sujay
  • 6,753
  • 2
  • 30
  • 49
  • Google Guava has some elegant functions for maps (http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#difference) but this is probably the fastest method. – Jason Sperske Sep 14 '12 at 04:58
  • @JasonSperske: Is there what OP is looking for? I don't see one. – Bhesh Gurung Sep 14 '12 at 05:02
  • I don't see it in Guava, I though there was something to the ImmutableMap.of("a", 1, "b", 2, "c", 3); code that might apply, but it's not a fit for the problem. You might look at Zipper in FunctionalJava (like Python's zip function which can combine two lists into a HashMap, but you don't get a performance advantage, it just lets you compose this code in a different way) – Jason Sperske Sep 14 '12 at 05:06
4

I purpose to you two very simple implementations. One with stream Api of Java 8, one without.

Java < 8 (without stream api)

if(keys.length != values.length) { 
    throw new IllegalArgumentException("Keys and Values need to have the same length."); 
}
Map<String,String> map = new HashMap<>();
for (int i = 0; i < keys.length; i++) {
    map.put(keys[i], values[i]);
}

Java > 8 (with stream api)

if(keys.length != values.length) { 
    throw new IllegalArgumentException("Keys and Values need to have the same length."); 
}
Map<String,String> map = IntStream.range(0, keys.length).boxed()
    .collect(Collectors.toMap(i -> keys[i], i -> values[i]));
Valentin Michalak
  • 2,089
  • 1
  • 14
  • 27
2

Constant time lookup from the start

If you are looking for a Map that retrieves the value associated with a key in constant time (meaning without having to look at most values), then you cannot do much faster, because the arrays need to be processed.

However, you can use a utility already written that way : com.google.common.collect.Maps.uniqueIndex

Instantaneous conversion, Linear time lookup

If you are ok with a Map that searches the array for the key every time, then you can create the Map instantly using your two arrays, by defining a new class that implements the Map interface :

class TwoArrayMap implements Map<String, String> {

   private final String[] keys;
   private final String[] values;
   // If you want to enable to add more key value pairs to your map, and
   // want to make the process faster, you could use ArrayLists instead of arrays

   public TwoArrayMap(String[] array1, String[] array2){
       if(array1 == null || array2 == null || array2.length < array1.length)
          throw new IllegalArgumentException();
       keys = array1;
       values = array2;
       // Alternatively, you could want to clone the arrays, to 
       // make sure they are not modified, using array1.clone(), etc
   }

   public String get(String key){

       for(int i=0; i<keys.length; i++)
             if(key == null && key == null || key != null && key.equals(k) )
                return values[i];
       return null;                     
   }

   public String put(String key, String Value) throws OperationNotSupportedException {
        throw new OperationNotSupportedException();
        // alternatively, you could resize the arrays and add a new key, or use an ArrayList
   }

}

Map<String, String> myMap = new TwoArrayMap(keys, values);


Lazy conversion, constant time lookup after conversion

Another approach would be to do it "lazily", meaning modify the above class, so that it keeps a reference to a HashMap internally, and fills it only when it is looking up elements :

class TwoArrayMap implements Map<String, String> {

   private final Map<String, String> hashmap;
   private int maxIndexAlreadyTransferred = -1;

   private final String[] keys;
   private final String[] values;

   public TwoArrayMap(String[] array1, String[] array2){
       if(array1 == null || array2 == null || array2.length < array1.length)
          throw new IllegalArgumentException();
       hashmap = new HashMap<>();
       keys = array1;
       values = array2;
       // Alternatively, you could want to clone the arrays, to 
       // make sure they are not modified, using array1.clone(), etc
   }

   public String get(String key){

       if(hashmap.containsKey(key))
            return hashmap.get(key);

       String k, value;
       while( maxIndexAlreadyTransferred + 1 < keys.length ){
             k = keys[ maxIndexAlreadyTransferred + 1 ];
             value = values[ maxIndexAlreadyTransferred +1 ];
             if(!hashmap.containsKey(k))
                 hashmap.put( k, value );
             maxIndexAlreadyTransferred++;
             if(key == null && k == null || key != null && key.equals(k) )
                return value;
       }
       return null;                     
   }

   public String put(String key, String Value) {
        hashmap.put(key, value);
   }

}

This solution would mean :

  • an instantaneous creation of your new object
  • linear time lookup for the first times you will query it, until everything is transferred
  • constant time lookup after that, behaving as a hash table
Vic Seedoubleyew
  • 9,888
  • 6
  • 55
  • 76
1

IMHO, it's highly unlikely that you will find a utility like that.

But, even if you find one chances are really low that it will provide any performance gain. Because, I think you won't able to do it without iterate through all the elements in both the arrays.

One thing I can suggest is (only if your arrays have a huge number of elements) that you can specify the capacity of the map while instantiating it to reduce overhead of resizing while you put entries into it.

Map<String, String> map = new HashMap<String, String>(keys.length);
//put keys and values into map ...
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
0

Convert two String arrays to Map in Java

import java.util.HashMap;
 public static void main(String[] args){
    String[] keys= {"a", "b", "c"};
    int[] vals= {1, 2, 3};
    HashMap<String, Integer> hash= new HashMap<String, Integer>();

    for(int i= 0; i < keys.length; i++){
      hash.put(keys[i], vals[i]);
    }
 }

Check this LINK for more solutions in different programming languages

Note : The keys should be unique..

Lucky
  • 16,787
  • 19
  • 117
  • 151