A very common operation on maps of collections is to create a new collection with an initial value when the key is not present, or if the key is present, do some function on the existing collection. Take for example a Map<String, Set<Integer>>, if the key is not there, create a Set with an initial value of 1. If the key is there, add the value map.size()+1 to the set (or replace this function with some other simple one-liner operation). In Java 7, it's straightforward with if/else, but pretty verbose. I can only come up with the below code for Java 8, which isn't much better (actually worse due to more lines of code). Is there a way to make this more concise?
public void process(Map<String, Set<Integer>> m, String key) {
m.compute(key, (k, v) -> {
if (v == null) {
v = new HashSet<>();
v.add(1);
return v;
} else {
v.add(v.size() + 1);
return v;
}
});
}
Here's another alternative:
Set<Integer> set = m.computeIfAbsent (key , k -> new HashSet<> ());
set.add(set.size() + 1);
The only reason this is a two liner (instead of one) is the need to obtain the current size of the Set in order to decide which value to add to it.
Not a one-liner unfortunately but it does its magic and is also directly more readable (downside : it creates a new HashSet<>() everytime)
m.putIfAbsent(key, new HashSet<>());
// Solution 1 :
m.compute(key, (k, v) -> {v.add(v.size() + 1); return v;});
// Solution 2 :
Set<Integer> s = m.get(key);
s.add(s.size() + 1);
Or as proposed by #Thilo and inspired by #Eran
m.computeIfAbsent(key, k -> new HashSet<>()).add(m.get(key).size() + 1);
The one liner is possible because it returns the value it computed as mentioned in the javadoc
If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.
There is even a similar example in the javadoc
map.computeIfAbsent(key, k -> new HashSet()).add(v);
The little trade-off of the liner is the extra call to m.get(key) which is not happening with the solution of #Eran
Set<Integer> v = m.getOrDefault(key, new HashSet<>());
v.add(v.size() + 1);
m.put(key, v);
Related
I need to create all possible combinations of some kind of Key, that is composed from X (in my case, 8), equally important elements. So i came up with code like this:
final LinkedList<Key> keys = new LinkedList();
firstElementCreator.getApplicableElements() // All creators return a Set of elements
.forEach( first -> secondElementCreator.getApplicableElements()
.forEach( second -> thirdElementCreator.getApplicableElements()
// ... more creators
.forEach( X -> keys.add( new Key( first, second, third, ..., X ) ) ) ) ) ) ) ) );
return keys;
and it's working, but there is X nested forEach and i have feeling that i'm missing out an easier/better/more elegant solution. Any suggestions?
Thanks in advance!
Is it Cartesian Product? Many libraries provide the API, for example: Sets and Lists in Guava:
List<ApplicableElements> elementsList = Lists.newArrayList(firstElementCreator, secondElementCreator...).stream()
.map(c -> c.getApplicableElements()).collect(toList());
List<Key> keys = Lists.cartesianProduct(elementsList).stream()
.map(l -> new Key(l.get(0), l.get(1), l.get(2), l.get(3), l.get(4), l.get(5), l.get(6), l.get(7))).collect(toList());
Since the number of input sets is fixed (it has to match the number of arguments in the Key constructor), your solution is actually not bad.
It's more efficient and easier to read without the lambdas, though, like:
for (Element first : firstElementCreator.getApplicableElements()) {
for (Element second : secondElementCreator.getApplicableElements()) {
for (Element third : thirdElementCreator.getApplicableElements()) {
keys.add(new Key(first, second, third));
}
}
}
The canonical solution is to use flatMap. However, the tricky part is to create the Key object from the multiple input levels.
The straight-forward approach is to do the evaluation in the innermost function, where every value is in scope
final List<Key> keys = firstElementCreator.getApplicableElements().stream()
.flatMap(first -> secondElementCreator.getApplicableElements().stream()
.flatMap(second -> thirdElementCreator.getApplicableElements().stream()
// ... more creators
.map( X -> new Key( first, second, third, ..., X ) ) ) )
.collect(Collectors.toList());
but this soon becomes impractical with deep nesting
A solution without deep nesting requires elements to hold intermediate compound values. E.g. if we define Key as
class Key {
String[] data;
Key(String... arg) {
data=arg;
}
public Key add(String next) {
int pos = data.length;
String[] newData=Arrays.copyOf(data, pos+1);
newData[pos]=next;
return new Key(newData);
}
#Override
public String toString() {
return "Key("+Arrays.toString(data)+')';
}
}
(assuming String as element type), we can use
final List<Key> keys =
firstElementCreator.getApplicableElements().stream().map(Key::new)
.flatMap(e -> secondElementCreator.getApplicableElements().stream().map(e::add))
.flatMap(e -> thirdElementCreator.getApplicableElements().stream().map(e::add))
// ... more creators
.collect(Collectors.toList());
Note that these flatMap steps are now on the same level, i.e. not nested anymore. Also, all these steps are identical, only differing in the actual creator, which leads to the general solution supporting an arbitrary number of Creator instances.
List<Key> keys = Stream.of(firstElementCreator, secondElementCreator, thirdElementCreator
/* , and, some, more, if you like */)
.map(creator -> (Function<Key,Stream<Key>>)
key -> creator.getApplicableElements().stream().map(key::add))
.reduce(Stream::of, (f1,f2) -> key -> f1.apply(key).flatMap(f2))
.apply(new Key())
.collect(Collectors.toList());
Here, every creator is mapping to the identical stream-producing function of the previous solution, then all are reduced to a single function combining each function with a flatMap step to the next one, and finally the resulting function is executed to get a stream, which is then collected to a List.
I have a map:
Map<String, List<Object>> dataMap;
Now i want to add new key value pairs to the map like below:
if(dataMap.contains(key)) {
List<Object> list = dataMap.get(key);
list.add(someNewObject);
dataMap.put(key, list);
} else {
List<Object> list = new ArrayList();
list.add(someNewObject)
dataMap.put(key, list);
}
How can i do this with Java8 functional style?
You can use computeIfAbsent.
If the mapping is not present, just create one by associating the key with a new empty list, and then add the value into it.
dataMap.computeIfAbsent(key, k -> new ArrayList<>()).add(someNewObject);
As the documentation states, it returns the current (existing or computed) value associated with the specified key so you can chain the call with ArrayList#add. Of course this assume that the values in the original map are not fixed-size lists (I don't know how you filled it)...
By the way, if you have access to the original data source, I would grab the stream from it and use Collectors.groupingBy directly.
This can be simplified by using the ternary operator. You don't really need the if-else statement
List<Object> list = dataMap.containsKey(key) ? dataMap.get(key) : new ArrayList<>();
list.add(someNewObject);
dataMap.put(key, list);
You can also use compute method.
dataMap.compute(key, (k, v) -> {
if(v == null)
return new ArrayList<>();
else {
v.add(someNewObject);
return v;
}
});
you can use
dataMap.compute(key,(k,v)->v!=null?v:new ArrayList<>()).add(someNewObject)
or
dataMap.merge(key,new ArrayList<>(),(v1,v2)->v1!=null?v1:v2).add(someNewObject)
Given a list of elements, I want to get the element with a given property and remove it from the list. The best solution I found is:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Is it possible to combine get and remove in a lambda expression?
To Remove element from the list
objectA.removeIf(x -> conditions);
eg:
objectA.removeIf(x -> blockedWorkerIds.contains(x));
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
str1.removeIf(x -> str2.contains(x));
str1.forEach(System.out::println);
OUTPUT:
A
B
C
Although the thread is quite old, still thought to provide solution - using Java8.
Make the use of removeIf function. Time complexity is O(n)
producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
API reference: removeIf docs
Assumption: producersProcedureActive is a List
NOTE: With this approach you won't be able to get the hold of the deleted item.
Consider using vanilla java iterators to perform the task:
public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
T value = null;
for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
if (test.test(value = it.next())) {
it.remove();
return value;
}
return null;
}
Advantages:
It is plain and obvious.
It traverses only once and only up to the matching element.
You can do it on any Iterable even without stream() support (at least those implementing remove() on their iterator).
Disadvantages:
You cannot do it in place as a single expression (auxiliary method or variable required)
As for the
Is it possible to combine get and remove in a lambda expression?
other answers clearly show that it is possible, but you should be aware of
Search and removal may traverse the list twice
ConcurrentModificationException may be thrown when removing element from the list being iterated
The direct solution would be to invoke ifPresent(consumer) on the Optional returned by findFirst(). This consumer will be invoked when the optional is not empty. The benefit also is that it won't throw an exception if the find operation returned an empty optional, like your current code would do; instead, nothing will happen.
If you want to return the removed value, you can map the Optional to the result of calling remove:
producersProcedureActive.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.map(p -> {
producersProcedureActive.remove(p);
return p;
});
But note that the remove(Object) operation will again traverse the list to find the element to remove. If you have a list with random access, like an ArrayList, it would be better to make a Stream over the indexes of the list and find the first index matching the predicate:
IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int) i));
With this solution, the remove(int) operation operates directly on the index.
Use can use filter of Java 8, and create another list if you don't want to change the old list:
List<ProducerDTO> result = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.collect(Collectors.toList());
I'm sure this will be an unpopular answer, but it works...
ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0] will either hold the found element or be null.
The "trick" here is circumventing the "effectively final" problem by using an array reference that is effectively final, but setting its first element.
With Eclipse Collections you can use detectIndex along with remove(int) on any java.util.List.
List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
integers.remove(index);
}
Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
If you use the MutableList type from Eclipse Collections, you can call the detectIndex method directly on the list.
MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
integers.remove(index);
}
Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
Note: I am a committer for Eclipse Collections
The below logic is the solution without modifying the original list
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
List<String> str3 = str1.stream()
.filter(item -> !str2.contains(item))
.collect(Collectors.toList());
str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]
When we want to get multiple elements from a List into a new list (filter using a predicate) and remove them from the existing list, I could not find a proper answer anywhere.
Here is how we can do it using Java Streaming API partitioning.
Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive
.stream()
.collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod)));
// get two new lists
List<ProducerDTO> matching = classifiedElements.get(true);
List<ProducerDTO> nonMatching = classifiedElements.get(false);
// OR get non-matching elements to the existing list
producersProcedureActive = classifiedElements.get(false);
This way you effectively remove the filtered elements from the original list and add them to a new list.
Refer the 5.2. Collectors.partitioningBy section of this article.
As others have suggested, this might be a use case for loops and iterables. In my opinion, this is the simplest approach. If you want to modify the list in-place, it cannot be considered "real" functional programming anyway. But you could use Collectors.partitioningBy() in order to get a new list with elements which satisfy your condition, and a new list of those which don't. Of course with this approach, if you have multiple elements satisfying the condition, all of those will be in that list and not only the first.
the task is: get ✶and✶ remove element from list
p.stream().collect( Collectors.collectingAndThen( Collector.of(
ArrayDeque::new,
(a, producer) -> {
if( producer.getPod().equals( pod ) )
a.addLast( producer );
},
(a1, a2) -> {
return( a1 );
},
rslt -> rslt.pollFirst()
),
(e) -> {
if( e != null )
p.remove( e ); // remove
return( e ); // get
} ) );
resumoRemessaPorInstrucoes.removeIf(item ->
item.getTipoOcorrenciaRegistro() == TipoOcorrenciaRegistroRemessa.PEDIDO_PROTESTO.getNome() ||
item.getTipoOcorrenciaRegistro() == TipoOcorrenciaRegistroRemessa.SUSTAR_PROTESTO_BAIXAR_TITULO.getNome());
Combining my initial idea and your answers I reached what seems to be the solution
to my own question:
public ProducerDTO findAndRemove(String pod) {
ProducerDTO p = null;
try {
p = IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int)i))
.get();
logger.debug(p);
} catch (NoSuchElementException e) {
logger.error("No producer found with POD [" + pod + "]");
}
return p;
}
It lets remove the object using remove(int) that do not traverse again the
list (as suggested by #Tunaki) and it lets return the removed object to
the function caller.
I read your answers that suggest me to choose safe methods like ifPresent instead of get but I do not find a way to use them in this scenario.
Are there any important drawback in this kind of solution?
Edit following #Holger advice
This should be the function I needed
public ProducerDTO findAndRemove(String pod) {
return IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int)i))
.orElseGet(() -> {
logger.error("No producer found with POD [" + pod + "]");
return null;
});
}
A variation of the above:
import static java.util.function.Predicate.not;
final Optional<MyItem> myItem = originalCollection.stream().filter(myPredicate(someInfo)).findFirst();
final List<MyItem> myOtherItems = originalCollection.stream().filter(not(myPredicate(someInfo))).toList();
private Predicate<MyItem> myPredicate(Object someInfo) {
return myItem -> myItem.someField() == someInfo;
}
i have this kind of data structure
Map<Integer, Integer> groupMap= new LinkedHashMap<>();
groupMap.put(10, 1);
groupMap.put(11, 0);
groupMap.put(14, 1);
groupMap.put(13, 0);
groupMap.put(12, 0);
groupMap.put(15, 1);
what can be the best way to find the key which has value 1 if i have a present key with one value.
Ex:i have key 14, now need to find the key 15 which has value 1
least looping will be helpfull.
my approch:
List<Integer> keys = new ArrayList<>();
keys.putAll(groupMap.keySet());
//getting the index of current key i have
int index = keys.indexOf(14);
if(keys.size() == index) return -1;
for(int i = index+1;i<keys.size();i++){
if(groupMap.get(i) == 1) return i;
}
i know it isn't a very good approach, but can you please suggest a good one.
This completely defeats the purpose of a key-value map. But if it's really what you want, I suppose you could do the following:
public static int getNextKeyByValue(int value, int previousKey) {
final Map<Integer, Integer> groupMap = new HashMap<>();
Iterator iterator = groupMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Integer> entry = (Map.Entry<Integer, Integer>) iterator.next();
if (entry.getValue() == value && entry.getKey() != previousKey) {
return entry.getKey();
}
}
return -1;
}
From the topic which #Titus mentioned in the comment, the most elegant and shortest solution is to use stream:
int getFirstCorrectValueBiggerThan (int lastValue) {
return groupMap.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), 1))
.map(Map.Entry::getKey)
.filter(value -> value > lastValue)
.findFirst();
}
edit:
sorry for the mistake, the code provided does not solve your problem since it is comparing keys not indexes. Here you have proper version, however it is not so cool anymore.
ArrayList<Integer> filteredList = groupMap.entrySet().stream()
.filter(entry -> entry.getValue().equals(1))
.map(Map.Entry::getKey)
.collect(Collectors.toCollection(ArrayList::new));
int nextCorrectElement = filteredList.get(filteredList.indexOf(14) + 1);
update
as far as i undestand what is written in this tutorial about map:
When a user calls put(K key, V value) or get(Object key), the function computes the index of the bucket in which the Entry should be. Then, the function iterates through the list to look for the Entry that has the same key (using the equals() function of the key).
and check out this topic about hash map complexity.
O(1) certainly isn't guaranteed - but it's usually what you should assume when considering which algorithms and data structures to use.
On top of that, the key part of your solution- the ArrayList::indexOf- is O(N) complex- you have to iterate through each element till the one which meets the condition. More info is in this topic.
So efectively you are iterating through every element of your hashmap anyway. And what is more, the hashmap searching (get method) is not quaranteed to be O(1) complex so there is a chance that you will double your work.
I have made a simple test of performance for stream based solution and simple loop proposed in this topic. In fact loop will be faster than sequential stream for each case I think, but still if you want that kind of performance gain then try to write it in in C++. Otherwise if you have more complex example then using the parallel stream may get some advantage due to higher abstraction level of the problem stating.
I have not really clear your question. If you are looking for all the tuples with value equals to 1, you could follow the approach below:
for (Entry<Integer, Integer> entry : groupMap.entrySet()) {
if (entry.getValue() == 1) {
System.out.println("The key is: " + entry.getKey().toString());
}
}
Do I have to return the object and then put a new one in ? Or can I just directly increment ?
Integer temp = myMap.get(key);
temp++;
myMap.put(key, temp);
there is no way to just do this (this doesn't work) :
myMap.get(key)++;
This is the shortest code that does this job.
myMap.put(key, myMap.get(key) + 1)
I think it is not too long.
In Java 8 there are new methods on Map which you can use with lambdas to solve this. First alternative, compute:
a.compute(key, (k, v) -> v+1);
Note that this only works if the hash is initialized for all possible keys.
If this is not guaranteed you can either change the above code to:
a.compute(key, (k, v) -> v == null ? 1 : v + 1);
Or use the merge method (which I would prefer):
a.merge(key, 1, (a, b) -> a + b);
Maybe there are more lambda based methods I am not aware of.
You can use a mutable integer such as AtomicInteger.
Map<Key, AtomicInteger> myMap = new HashMap<Key, AtomicInteger>();
myMap.get(key).incrementAndGet();
Or you can use Trove4j which supports primitives in collections.
TObjectIntHashMap<Key> myMap;
myMap.increment(key);
Do I have to return the object and then put a new one in ?
As long as you use the Integer wrapper class yes, because it's immutable. You could use a mutable wrapper class instead, even one that has an increment() method. However, you then lose the ability to use autoboxing and autounboxing on the values.
You can't directly increment it, because it is immutable. You have to increment it and put the new object back.
Auto boxing is also interfering here. In fact what's happening is something similar to:
Integer i1 = getFromMap();
i1 = Integer.valueOf(++ i1.intValue());
So here your reference points to a new object. You have to put that object back in the map, under the same key.
As Integer are immutable, yes, you have to do it that way.
If you really want to increment it directly, you'll have to write your own mutable class.
If you have to do this more than twice you'd prefer to create a tiny class like:
public class MappedCounter {
private Map<String, Integer> map = new HashMap<String, Integer>();
public void addInt(String k, int v) {
if (!map.containsKey(k)) map.put(k, v);
else map.put(k, map.get(k) + v);
}
public int getInt(String k) {
return map.containsKey(k) ? map.get(k) : 0;
}
public Set<String> getKeys() {
return map.keySet();
}
}
Here are solutions using a Map (Java 8+), and a primitive Map and Bag using Eclipse Collections (EC).
JDK Map
Map<String, Integer> map = new HashMap<>();
map.merge("item", 1, Integer::sum);
Integer count = map.getOrDefault("item", 0);
EC Primitive Map
MutableObjectIntMap<String> map = ObjectIntMaps.mutable.empty();
map.addToValue("item", 1);
int count = map.getIfAbsent("item", 0);
EC Bag
MutableBag<String> bag = Bags.mutable.empty();
bag.add("item");
int count = bag.occurrencesOf("item");
The benefit of the primitive Map or Bag (which wraps a primitive Map) is that there is no boxing of the count values, and adding is explicit in both method names (addToValue / add). A Bag is a better data structure IMO if you want to simply count things.
Note: I am a committer for Eclipse Collections.
First of all: be aware of unboxing: the temp is from type Integer. But the operation ++ is implemented for int. So temp is unboxed to type int. This means if temp is null you run in a NullPointerException.
And you have to do it like you discripted in your first code block.
I use the below code and it works but at the beginning you need to define a BiFunction describing that the operation is incrementing by 1.
public static Map<String, Integer> strInt = new HashMap<String, Integer>();
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> bi = (x,y) -> {
if(x == null)
return y;
return x+y;
};
strInt.put("abc", 0);
strInt.merge("abc", 1, bi);
strInt.merge("abc", 1, bi);
strInt.merge("abc", 1, bi);
strInt.merge("abcd", 1, bi);
System.out.println(strInt.get("abc"));
System.out.println(strInt.get("abcd"));
}
output is
3
1
Just for completeness in Java 8 there is a longAdder which brings some benefits in comparison to AtomicInteger (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html)
final Map<WhatEver, LongAdder> result = new HashMap<>();
result.get(WhatEver).increment();
This should work
// If the key you want to add does not exist then add it as a new key
// And make the value 1
if (map.get(key) == null) {
map.put(key, 1);
} else {
// If the key does exist then replace the key's value with it's
// Original value plus one
map.put(key, map.get(key) + 1);
}
Found this to be the best way, avoiding NPE.
Map<Integer, Integer> map = new HashMap<>();
map.put(5, map.getOrDefault(5, 0) + 1);
System.out.println(map.get(5));
Output:
1