What is the easiest way to convert a HashMap into a 2D array?
HashMap map = new HashMap();
Object[][] arr = new Object[map.size()][2];
Set entries = map.entrySet();
Iterator entriesIterator = entries.iterator();
int i = 0;
while(entriesIterator.hasNext()){
Map.Entry mapping = (Map.Entry) entriesIterator.next();
arr[i][0] = mapping.getKey();
arr[i][1] = mapping.getValue();
i++;
}
This can only be done when the types of both key and value are the same.
Given:
HashMap<String,String> map;
I can create an array from this map with this simple loop:
String[][] array = new String[map.size()][2];
int count = 0;
for(Map.Entry<String,String> entry : map.entrySet()){
array[count][0] = entry.getKey();
array[count][1] = entry.getValue();
count++;
}
How about
Object[][] array = new Object[][]{map.keySet.toArray(), map.entrySet.toArray()};
Or, to be more specific about the types, let's say they're Strings: Set's toArray takes a hint argument, so that
String[][] array = new String[][]{map.keySet.toArray(new String[0]), map.entrySet.toArray(new String[0])};
Edit: I just realized a couple of days later that while this may work by chance, in general it shouldn't. The reason is the intermediate Set; although it is "backed by the map", there seems to be no explicit guarantee that it will iterate in any particular order. Thus the key- and entry-arrays might not be in the same order, which is a disaster for sure!
Using Java 8 stream:
#Test
void testMapToArray() {
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", 2);
Object[][] arr =
map.entrySet().stream()
.map(e -> new Object[]{e.getKey(), e.getValue()})
.toArray(Object[][]::new);
System.out.println(Arrays.deepToString(arr));
}
Output:
[[key1, value1], [key2, 2]]
Iterate over your Map using entrySet, and fill your array record with the Entry object
Related
I am sorting a populated set of MyObject (the object has a getName() getter) in a stream using a predefined myComparator.
Then once sorted, is there a way to collect into a map the name of the MyObject and the order/position of the object from the sort?
Here is what I think it should look like:
Set<MyObject> mySet; // Already populated mySet
Map<String, Integer> nameMap = mySet.stream()
.sorted(myComparator)
.collect(Collectors.toMap(MyObject::getName, //HowToGetThePositionOfTheObjectInTheStream));
For example, if the set contain three objects (object1 with name name1, object2 with name name2, object3 with name name3) and during the stream they get sorted, how do I get a resulting map that looks like this:
name1, 1
name2, 2
name3, 3
Thanks.
A Java Stream doesn't expose any index or positioning of elements, so I know no way of replacing /*HowToGetThePositionOfTheObjectInTheStream*/ with streams magic to obtain the desired number.
Instead, one simple way is to collect to a List instead, which gives every element an index. It's zero-based, so when converting to a map, add 1.
List<String> inOrder = mySet.stream()
.sorted(myComparator)
.map(MyObject::getName)
.collect(Collectors.toList());
Map<String, Integer> nameMap = new HashMap<>();
for (int i = 0; i < inOrder.size(); i++) {
nameMap.put(inOrder.get(i), i + 1);
}
Try this one. you could use AtomicInteger for value of each entry of map. and also to guarantee order of map use LinkedHashMap.
AtomicInteger index = new AtomicInteger(1);
Map<String, Integer> nameMap = mySet.stream()
.sorted(myComparator)
.collect(Collectors
.toMap(MyObject::getName, value -> index.getAndIncrement(),
(e1, e2) -> e1, LinkedHashMap::new));
The simplest solution would be a loop, as a formally correct stream solution that would also work in parallel requires a nontrivial (compared to the rest) merge functions:
Map<String,Integer> nameMap = mySet.stream()
.sorted(myComparator)
.collect(HashMap::new, (m, s) -> m.put(s.getName(), m.size()),
(m1, m2) -> {
int offset = m1.size();
m2.forEach((k, v) -> m1.put(k, v + offset));
});
Compare with a loop/collection operations:
List<MyObject> ordered = new ArrayList<>(mySet);
ordered.sort(myComparator);
Map<String, Integer> result = new HashMap<>();
for(MyObject o: ordered) result.put(o.getName(), result.size());
Both solutions assume unique elements (as there can be only one position). It’s easy to change the loop to detect violations:
for(MyObject o: ordered)
if(result.putIfAbsent(o.getName(), result.size()) != null)
throw new IllegalStateException("duplicate " + o.getName());
Dont use a stream:
List<MyObject> list = new ArrayList<>(mySet);
list.sort(myComparator);
Map<String, Integer> nameMap = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
nameMap.put(list.get(i).getName(), i);
}
Not only will this execute faster than a stream based approach, everyone knows what's going on.
Streams have their place, but pre-Java 8 code does too.
Let's say I have a HashMap with String keys and Integer values:
map = {cat=1, kid=3, girl=3, adult=2, human=5, dog=2, boy=2}
I want to switch the keys and values by putting this information into another HashMap. I know that a HashMap cannot have duplicate keys, so I tried to put the information into a HashMap with the Integer for the keys that would map to a String ArrayList so that I could potentially have one Integer mapping to multiple Strings:
swap = {1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
I tried the following code:
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (String x : map.keySet()) {
for (int i = 0; i <= 5; i++) {
ArrayList<String> list = new ArrayList<String>();
if (i == map.get(x)) {
list.add(x);
swap.put(i, list);
}
}
}
The only difference in my code is that I didn't hard code the number 5 into my index; I have a method that finds the highest integer value in the original HashMap and used that. I know it works correctly because I get the same output even if I hard code the 5 in there, I just didn't include it to save space.
My goal here is to be able to do this 'reversal' with any set of data, otherwise I could just hard code the value. The output I get from the above code is this:
swap = {1=[cat], 2=[boy], 3=[girl], 5=[human]}
As you can see, my problem is that the value ArrayList is only keeping the last String that was put into it, instead of collecting all of them. How can I make the ArrayList store each String, rather than just the last String?
With Java 8, you can do the following:
Map<String, Integer> map = new HashMap<>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
Map<Integer, List<String>> newMap = map.keySet()
.stream()
.collect(Collectors.groupingBy(map::get));
System.out.println(newMap);
The output will be:
{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
you are recreating the arrayList for every iteration and i can't figure out a way to do it with that logic, here is a good way though and without the need to check for the max integer:
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
List<String> get = swap.get(value);
if (get == null) {
get = new ArrayList<>();
swap.put(value, get);
}
get.add(key);
}
Best way is to iterate over the key set of the original map.
Also you have to asure that the List is present for any key in the target map:
for (Map.Entry<String,Integer> inputEntry : map.entrySet())
swap.computeIfAbsent(inputEntry.getValue(),()->new ArrayList<>()).add(inputEntry.getKey());
This is obviously not the best solution, but approaches the problem the same way you did by interchanging inner and outer loops as shown below.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (Integer value = 0; value <= 5; value++) {
ArrayList<String> list = new ArrayList<String>();
for (String key : map.keySet()) {
if (map.get(key) == value) {
list.add(key);
}
}
if (map.containsValue(value)) {
swap.put(value, list);
}
}
Output
{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
Best way I can think of is using Map.forEach method on existing map and Map.computeIfAbsent method on new map:
Map<Integer, List<String>> swap = new HashMap<>();
map.forEach((k, v) -> swap.computeIfAbsent(v, k -> new ArrayList<>()).add(k));
As a side note, you can use the diamond operator <> to create your new map (there's no need to repeat the type of the key and value when invoking the map's constructor, as the compiler will infer them).
As a second side note, it's good practice to use interface types instead of concrete types, both for generic parameter types and for actual types. This is why I've used List and Map instead of ArrayList and HashMap, respectively.
Using groupingBy like in Jacob's answer but with Map.entrySet for better performance, as suggested by Boris:
// import static java.util.stream.Collectors.*
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(groupingBy(Entry::getValue, mapping(Entry::getKey, toList())));
This uses two more methods of Collectors: mapping and toList.
If it wasn't for these two helper functions, the solution could look like this:
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
groupingBy(
Entry::getValue,
Collector.of(
ArrayList::new,
(list, e) -> {
list.add(e.getKey());
},
(left, right) -> { // only needed for parallel streams
left.addAll(right);
return left;
}
)
)
);
Or, using toMap instead of groupingBy:
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
toMap(
Entry::getValue,
(e) -> new ArrayList<>(Arrays.asList(e.getKey())),
(left, right) -> {
left.addAll(right);
return left;
}
)
);
It seams you override the values instrad of adding them to the already creared arraylist. Try this:
HashMap<Integer, ArrayList<String>> swapedMap = new HashMap<Integer, ArrayList<String>>();
for (String key : map.keySet()) {
Integer swappedKey = map.get(key);
ArrayList<String> a = swapedMap.get(swappedKey);
if (a == null) {
a = new ArrayList<String>();
swapedMap.put(swappedKey, a)
}
a.add(key);
}
I didn't have time to run it (sorry, don't have Java compiler now), but should be almost ok :)
You could use the new merge method in java-8 from Map:
Map<Integer, List<String>> newMap = new HashMap<>();
map.forEach((key, value) -> {
List<String> values = new ArrayList<>();
values.add(key);
newMap.merge(value, values, (left, right) -> {
left.addAll(right);
return left;
});
});
I have got an Arraylist of strings and I need to return the Arraylist indexes of strings that are the same.
For example
Arraylist[0]: IPAddress
Arraylist[1]: DomainName
Arraylist[2]: IPAddress
Arraylist[3]: Filesize
The output should be:
Arraylist[0]
IPAddress|0,2 //0,2 denotes the arraylist index that is of the same
Arraylist[1]
DomainName|1
Arraylist[2]
Filesize|3
Any idea how can this be achieved?
What I have done is:
for(int i=0; i<arr.size(); i++){
if(arr.get(i).equals(arr.size()-1)){
//print index
}
}
With Java8 streams
List<String> strings = Arrays.asList("IPAddress", "DomainName", "IPAddress", "Filesize");
Map<String, List<Integer>> map = IntStream.range(0, strings.size()).boxed().collect(Collectors.groupingBy(strings::get));
System.out.println(map);
output
{DomainName=[1], Filesize=[3], IPAddress=[0, 2]}
To get the results in ordered
Map<String, List<Integer>> map = IntStream.range(0, strings.size())
.boxed()
.collect(Collectors.groupingBy(strings::get, LinkedHashMap::new, Collectors.toList()));
The mechanical steps are fairly straightforward:
Get a collection which can support a key (which is the string in your list) and a list of values representing the indexes in which they occur (which would be another ArrayList).
If the element exists in the collection, simply add the index to its value.
Otherwise, create a new list, add the index to that, then add that to the collection.
Here is some sample code below.
final List<String> list = new ArrayList<String>() {{
add("IPAddress");
add("DomainName");
add("IPAddress");
add("Filesize");
}};
final Map<String, List<Integer>> correlations = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
final String key = list.get(i);
if (correlations.containsKey(key)) {
correlations.get(key).add(i);
} else {
final List<Integer> indexList = new ArrayList<>();
indexList.add(i);
correlations.put(key, indexList);
}
}
Any optimizations to the above are left as an exercise for the reader.
I've a SortedMap<String, String> containing key:ID & value:Name.
Now I want to rearrange the elements of this Map in random fashion & store them in separate map.
// Get a random entry from the SortedMap.
String[] keyArr = student.keySet().toArray();
String key = keyArr[new Random().nextInt(keyArr.length)];
// Use a separate List<String> to store which key has been selected, so that they are not re-selected
But above method does not sound very efficient.
Please suggest.
Thank You
You would need to copy the entrySet into a List and shuffle it. This would give you the elements in a random order.
Now you can push those elements to a new LinkedHashMap - to preserve the random order. Something like the following:
final Map<String, Object> m = new TreeMap<>();
m.put("A", 1);
m.put("B", 1);
m.put("C", 1);
m.put("D", 1);
m.put("E", 1);
m.put("F", 1);
m.put("G", 1);
final List<Map.Entry<String, Object>> e = new ArrayList<>(m.entrySet());
Collections.shuffle(e);
final Map<String, Object> r = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : e) {
r.put(entry.getKey(), entry.getValue());
}
I'm not sure that I got your question, but you should be able to do something like
Map<String, String> result = new LinkedHashMap<>();
List keys = new ArrayList(map.keySet());
Collections.shuffle(keys);
for (Object o : keys) {
// Access keys/values in a random order
result.put(o, map.get(o));
}
What is the best way to convert array elements as keys in a new dictionary ?
This isn't much to go on, but let's see:
Say you have an int[10] and you want to store them in a HashMap (Dictionaries aren't used much these days). You could do this:
Map<Integer, Integer> dict = new HashMap<Integer, Integer>();
for(int i = 0; i < array.length; i++) {
dict.put(i,array[i]); // autoboxing takes care of primitives
}
If you were looking to use the actual values in the array as the keys in the dictionary, then you have to ask yourself "Where are my dictionary values coming from?"
For example, if you wanted the values to be the index, you could just reverse the above
dict.put(array[i],i);
Perhaps you have another array with some values in it.
int[] intArray = ...
String[] strArray = ...
Map<Integer, String> dict = new HashMap<Integer, String>();
for(int i = 0; i < array.length; i++) {
dict.put(intArray[i], strArray[i]); // autoboxing takes care of primitives
}
Simply put, you have a put method. The first parameter is going to be the key, the second parameter is going to be the value. As long as you're iterating over the array you'll have your keys; then it's just a matter of deciding what values go in there.
Integer[] keys = {1, 2, 3};
HashMap<Integer, Object> map = new HashMap<Integer, Object>();
Collections.addAll( map.keySet(), keys );