I am trying to add pairs that add up to a certain number in java and one of the ways I am trying to attempt this is to create a double ArrayList within my HashMap. If I add 1 and 2 to my list, I will get 3 as my key. For example:
HashMap<Integer, ArrayList<ArrayList<Integer>>> map = new HashMap<>();
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
map.put(list.get(0) + list.get(1), new ArrayList<>(list));
Output looks like this
Key: 3 Value: [[1,2]]
If I were to add one more pair
Key: 3 Value: [[1,2],[0,3]]
but I keep getting a 'method is not applicable in the type HashMap<Integer,ArrayList<ArrayList>> is not applicable for the arguments (int, new ArrayList<>(list))'
I've also tried
new ArrayList<>(new ArrayList<>(list))
thinking that I might need to initialize the bigger matrix first but I end up with the same error sadly.
This line:
new ArrayList<>(list)
creates a flat ArrayList<Integer>, while the HashMap is expecting ArrayList<ArrayList<Integer>>. By the same token, new ArrayList<>(new ArrayList<>(list)) also creates a flat Integer list because you are just doing the same thing twice. See the API document for ArrayList
This is one way that would work given the 2-D list setup:
HashMap<Integer, List<List<Integer>>> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
List<List<Integer>> outer = new ArrayList<>();
outer.add(list);
map.put(list.get(0) + list.get(1), outer);
You could also create some lambdas which may facilitate this. For example.
Map<Integer, List<List<Integer>>> map1 = new HashMap<>();
Create a function to sum the elements of a list.
Function<List<Integer>, Integer> sum =
list -> list.stream()
.mapToInt(Integer::intValue).sum();
Then create a BiConsumer to take the list pair and existing map and add them if need be. The computeIfAbsent, enters a value for the key if the key was null or absent. The list is returned so the the pair can be added to the newly created list.
BiConsumer<List<Integer>, Map<Integer,
List<List<Integer>>>> addToMap =
(pair, map) -> {
map.computeIfAbsent(sum.apply(pair),
v -> new ArrayList<>()).add(pair);
};
Putting it all together.
addToMap.accept(List.of(1,2), map1);
addToMap.accept(List.of(0,4), map1);
addToMap.accept(List.of(1,5), map1);
addToMap.accept(List.of(0,3), map1);
addToMap.accept(List.of(-1,5), map1);
addToMap.accept(List.of(-1,2,3),map1);
map1.entrySet().forEach(System.out::println);
prints
3=[[1, 2], [0, 3]]
4=[[0, 4], [-1, 5], [-1, 2, 3]]
6=[[1, 5]]
As you can see, this doesn't enforce any size limitations on the "pairs."
This may be overkill for what you want but there may be some elements you can put to use. Also note that List.of above is immutable.
Related
I'm having a hard time converting a Map containing some integers as keys and a list of random strings as values.
E.g. :
1 = ["a", "b", "c"]
2 = ["a", "b", "z"]
3 = ["z"]
I want to transform it into a Map of distinct strings as keys and lists the integers as values.
E.g. :
a = [1, 2]
b = [1, 2]
c = [1]
z = [2,3]
Here's what I have so far:
Map<Integer, List<String>> integerListMap; // initial list is already populated
List<String> distinctStrings = new ArrayList<>();
SortedMap<String, List<Integer>> stringListSortedMap = new TreeMap<>();
for(Integer i: integers) {
integerListMap.put(i, strings);
distinctStrings.addAll(strings);
}
distinctStrings = distinctStrings.stream().distinct().collect(Collectors.toList());
for(String s : distinctStrings) {
distinctStrings.put(s, ???);
}
Iterate over your source map's value and put each value into the target map.
final Map<String, List<Integer>> target = new HashMap<>();
for (final Map.Entry<Integer, List<String>> entry = source.entrySet()) {
for (final String s : entry.getValue()) {
target.computeIfAbsent(s, k -> new ArrayList<>()).add(entry.getKey());
}
}
Or with the Stream API by abusing Map.Entry to build the inverse:
final Map<String, List<Integer>> target = source.entrySet()
.stream()
.flatMap(e -> e.getValue().stream().map(s -> Map.entry(s, e.getKey()))
.collect(Collectors.groupingBy(e::getKey, Collectors.mapping(e::getValue, Collectors.toList())));
Although this might not be as clear as introducing a new custom type to hold the inverted mapping.
Another alternative would be using a bidirectial map. Many libraries come implementations of these, such as Guava.
There's no need to apply distinct() since you're storing the data into the Map and keys are guaranteed to be unique.
You can flatten the entries of the source map, so that only one string (let's call it name) and a single integer (let's call it number) would correspond to a stream element, and then group the data by string.
To implement this problem using streams, we can utilize flatMap() operation to perform one-to-many transformation. And it's a good practice to define a custom type for that purpose as a Java 16 record, or a class (you can also use a Map.Entry, but note that approach of using a custom type is more advantages because it allows writing self-documenting code).
In order to collect the data into a TreeMap you can make use of the three-args version of groupingBy() which allows to specify mapFactory.
record NameNumber(String name, Integer number) {}
Map<Integer, List<String>> dataByProvider = Map.of(
1, List.of("a", "b", "c"),
2, List.of("a", "b", "z"),
3, List.of("z")
);
NavigableMap<String, List<Integer>> numbersByName = dataByProvider.entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.map(name -> new NameNumber(name, entry.getKey()))
)
.collect(Collectors.groupingBy(
NameNumber::name,
TreeMap::new,
Collectors.mapping(NameNumber::number, Collectors.toList())
));
numbersByName.forEach((name, numbers) -> System.out.println(name + " -> " + numbers));
Output:
a -> [2, 1]
b -> [2, 1]
c -> [1]
z -> [3, 2]
Sidenote: while using TreeMap it's more beneficial to use NavigableMap as an abstract type because it allows to access methods like higherKey(), lowerKey(), firstEntry(), lastEntry(), etc. which are declared in the SortedMap interface.
I have 2 lists which contain Objects. These 2 lists can have same Objects in different order.
I have overridden the equals method in the Object such that if one particular property of the object is same as other Object then they are equal even if other properties are not same.
Now I need to create a map where the key is the Object from one list and value is the same Object from the other list. If there is an Object in one list which does not have an equal object in the other list, those Objects should be ignored while creating the Map.
How can i accomplish this using java stream?
I can see why you want to do something like this, but it seems very hacky. I'm sure if you tell us what you're trying to achieve, we can provide you with a better design. Regardless, the following will work with Java 10:
var list1 = List.of("One", "Two", "Three");
var list2 = List.of("Two", "Three", "Four");
var set = Set.copyOf(list2);
var map = list1.stream()
.filter(set::contains)
.collect(Collectors.toMap(k -> k, v -> list2.get(list2.indexOf(v))));
System.out.println(map);
Output:
{Two=Two, Three=Three}
The equivalent code written for Java 8 is as follows:
List<String> list1 = Arrays.asList("One", "Two", "Three");
List<String> list2 = Arrays.asList("Two", "Three", "Four");
Set<String> set = new HashSet<>(list2);
// Defined the Map here for formatting reasons.
Map<String, String> map;
map = list1.stream()
.filter(set::contains)
.collect(Collectors.toMap(k -> k, v -> list2.get(list2.indexOf(v))));
System.out.println(map);
Note: This assumes that you have overridden Object#hashCode as well as Object#equals.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
The arrival of Java 9 brings many new features to Java's Collections API, one of which being collection factory methods.
What are they and how can I implement them properly?
Note 1: To prevent the use of raw-types, I have opted to provide a generic type for each class that I mention below by using E, representing an element of a Collection<E>.
Note 2: This answer is subject to change; please edit this post if a typo has occurred.
What are collection factory methods?
A collection factory method in Java is a static method that provides a simple way of initializing an immutable Collection<E>.
Being immutable, no elements can be added to, removed from, or modified inside the Collection<E> after it is initialized.
With Java 9, collection factory methods are provided for the following interfaces: List<E>, Set<E>, and Map<K, V>
What do they improve?
Up until Java 9, there has been no simple, universal method to initialize a Collection<E> with initial elements/key-value entries. Previously, developers were required to initialize them as follows (assuming the generic types E, K, and V have been replaced with Integer):
List<Integer>
The following method is arguably the simplest to initialize a List<Integer> with initial elements, however the result is simply a view of a List<Integer>; we are unable to add to or remove from this List<Integer>, but we are still able to modify existing elements by using List#set.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
If we wanted our List<Integer> to be entirely mutable, then we would have to pass it to the constructor of an ArrayList<Integer>, for example:
List<Integer> mutableList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer>
A Set<Integer> required more code to initialize with initial elements than a List<Integer> does (seeing as a List<Integer> is required to initialize a Set<Integer> with initial elements), which can be seen below.
Set<Integer> mutableSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Map<Integer, Integer>
A Map<Integer, Integer> is arguably the most complicated to initialize with initial key-value entries; however, there are multiple ways to go about it.
One method was to first initialize an empty Map<Integer, Integer> and simply call Map#put to add key-value entries.
Another method was to use an anonymous class with two curly braces, which would still require Map#put to be called.
Why should I use them?
I argue that collection factory methods provide the developer with a concise method of initializing a List<E>, Set<E>, or Map<K, V> with initial elements/key-value entries, which can be seen by the examples below.
What is the proper syntax to use?
For simplicity, these examples will replace the generic types E, K, and V with Integer.
List<Integer>
List<Integer> list = List.of();
Initializes an empty, immutable List<Integer>.
List<Integer> list = List.of(1);
Initializes an immutable List<Integer> with one element.
List<Integer> list = List.of(1, 2);
Initializes an immutable List<Integer> with two elements.
List<Integer> list = List.of(1, 2, 3, 4, 5, ...);
Initializes an immutable List<Integer> with a variable amount of elements.
Set<Integer>
Set<Integer> set = Set.of();
Initializes an empty, immutable Set<Integer>.
Set<Integer> set = Set.of(1);
Initializes an immutable Set<Integer> with one element.
Set<Integer> set = Set.of(1, 2);
Initializes an immutable Set<Integer> with two elements.
Set<Integer> set = Set.of(1, 2, 3, 4, 5, ...);
Initializes an immutable Set<Integer> with a variable amount of elements.
Map<Integer, Integer>
Map<Integer, Integer> map = Map.of();
Initializes an empty, immutable Map<Integer, Integer>.
Map<Integer, Integer> map = Map.of(1, 2);
Initializes an immutable Map<Integer, Integer> with one key-value entry.
Note that the key is 1 and the value is 2.
Map<Integer, Integer> map = Map.of(1, 2, 3, 4);
Initializes an immutable Map<Integer, Integer> with two key-value entries.
Note that the keys are 1 and 3 and the values are 2 and 4.
Map<Integer, Integer> map = Map.ofEntries(Map.entry(1, 2), Map.entry(3, 4), ...);
Initializes an immutable Map<Integer, Integer> with a variable amount of key-value entries.
As you can see, this new method of initialization requires less code than its predecessors.
Can I use collection factory methods to create mutable objects?
The Collection<E> created by collection factory methods are inherently immutable, however we are able to pass them to a constructor of an implementation of the Collection<E> to produce a mutable version:
List<Integer>
List<Integer> mutableList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
Set<Integer>
Set<Integer> mutableSet = new HashSet<>(Set.of(1, 2, 3, 4, 5));
Map<Integer, Integer>
Map<Integer, Integer> mutableMap = new HashMap<>(Map.of(1, 2, 3, 4));
I am having an arraylist which contains a list of numbers. I want to get all the values from the HashMap which has the keys which are in the array list.
For example say the array list contains 1,2,3,4,5,6,7,8,9 list
I want to get all the values for the keys 1,2,3,4,5,6,7,8,9 map
So currently I am implementing
for (i=0;i<list.size;i++){
map_new.put(list.get(),map.get(list.get()))
}
Is there any efficient way to do this?
Your code basically assumes that map.get(list.get()) always returns a value, you can try the following code which first filters the not null values from the list object and then adds to the new Map:
Map<String, Integer> newMap = list.stream().
filter(key -> (map.get(key) != null)).//filter values not present in Map
collect(Collectors.toMap(t -> t, t -> map.get(t)));//now collect to a new Map
In case, if map.get(list.get()) returns null, your code creates a Map with null values in it for which you might end up doing null checks, which is not good, rather you can ensure that your newly created Map always contains a value for each key.
Assuming the signature of list and the map are as following
List<Integer> list;
Map<Integer, Integer> map;
You can use following
for(int a : list){
Integer b = map.get(a);
if(b != null)
// b is your desired value you can store in another collection
}
Which is similar to the procedure you have already used.
As you can access the map in O(1) so the complexity of this code will be O(listsize)
There is not much you can do for efficiency. Still couple of small things you can do considering code example you have given above:
1) Change your for loop to
for(Long num : list)
instead of iterating using index, this will reduce you get calls over list.
2) You can update the existing map , so that you even do not need to iterate.
map.keySet().retainAll(list);
for(Long key: map.keySet()) {
System.out.println(map.get(key));
}
With this existing map will contain only those data whose keys are present in list, but you should use it carefully depending upon rest of the code logic.
You can capitalize on the fact that the keyset of a map is backed by the map itself and modifications to the keyset will reflect back to the map itself. This way, you can use the retainAll() method of the Set interface to reduce the map with a single line of code. Here is an example:
final Map<Integer, String> m = new HashMap<Integer, String>();
m.put(1, "A");
m.put(2, "B");
m.put(3, "C");
m.put(4, "D");
m.put(5, "E");
final List<Integer> al = Arrays.asList(new Integer[] { 2, 4, 5 });
System.out.println(m);
m.keySet().retainAll(al);
System.out.println(m);
This will output:
{1=A, 2=B, 3=C, 4=D, 5=E}
{2=B, 4=D, 5=E}
Is there any ready implementation of super MultiValue map?
I need a map
SuperMap<List<String>, List<String>> superMap
where key List is set of keys, and value List is set of values.
keys: {a, b, c} and values for any of these keys are values: {1, 2, 3, 4, 5}
It means that key a should have a values {1, 2, 3, 4, 5} as well as the key b and c.
Updated with requirements from comments
I need to get all keys of one group? For example I need to get collection of keys for one similar value. In that case I cannot use your approach map.put("a", value); because I need to group (a, b it is first group, and c is related to second group).
UPD2
I'm looking for a simple and much concise solution to replace this code
Map<List<String>, List<String>> ops = new HashMap<List<String>, List<String>>() {{
put(asList("a", "aaa"), asList("1", "2"));
put(asList("b", "bbb"), asList("3", "4"));
}};
public static List<String> values(String optionKey) {
for (List<String> key : ops.keySet()) {
for (String k : key) {
if (optionKey.equals(k)) {
return ops.get(key);
}
}
}
return Collections.emptyList();
}
with some smart impl form any well known lib (Google Guava or something).
I think you're overcomplicating your data structure. You can simply use HashMap or similar implementation.
Map<String, List<String>> map = new LinkedHashMap<>();
List<String> value = new ArrayList<>();
// Fill the value list here...
map.put("a", value);
map.put("b", value);
map.put("c", value);