java stream is closed - java

I want to use stream getfirst method two times but an error occurred says (java.lang.IllegalStateException: stream has already been operated upon or closed) and this stream code begins at comment named here.
//code
Stream<Map.Entry<String,Integer>> sorted =
map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
Supplier<Stream<Map.Entry<String,Integer>>> sort2 = () -> sorted;
Optional<String> kk = Optional.of(sort2.get().findFirst().get().getKey());
Optional<Integer> vv = Optional.of(sort2.get().findFirst().get().getValue());
int vmax = vv.get().intValue() ;
int count=0;
ArrayList<String> a = new ArrayList<String>() ;
for(Map.Entry<String,Integer> h: map.entrySet() ) {
if(h.getValue()==vmax) {
a.add(h.getKey()) ;
count++;
}
}
if(count>1) {
Collections.sort(a);
System.out.println(a.get(0));
}
else {
System.out.println(kk.get());
}
map.clear();
}
}
}

You can't use a stream twice. Create a variable to hold the value of findFirst()

sort2 is not doing anything for you, and simply hiding the issue that you can't reuse sorted.
In this situation, by far the best solution is to store sorted.findFirst() in a variable and reuse it rather than call findFirst() twice. If you wanted to do it like this, then you would have to write
Supplier<Stream<Map.Entry<String,Integer>>> sort2 = () -> map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
...which would sort the entries twice, which would be very inefficient.

Stream.findFirst is a terminal operation - calling findFirst twice on the same stream like you are doing is not possible.
It looks like you don't need to call it twice: even if you restarted a new stream, both times you would expect to get the same map entry. So call findFirst only once, and use the Optional it returns to you:
Optional<Map.Entry<String, Integer>> firstEntry = sorted.findFirst();
Optional<String> kk = firstEntry.map(Map.Entry::getKey);
Optional<Integer> vv = firstEntry.map(Map.Entry::getValue);
Note that there's a better way to get the maximum item from the stream than sorting: using the max operator.
Optional<Map.Entry<String, Integer>> maxEntry = map.entrySet().stream()
.max(Map.Entry.comparingByValue());

Related

Java Map getValue not possible

I got a code which gets all minimum values from a list called frequencies. Then it puts the min values with the percentage of total values into a String. To calculate the percentage I want to call minEntryes.getValue()(minEntryes is the Map<String, Integer> with all the min values in it), but it does not work. My code:
StringBuilder wordFrequencies = new StringBuilder();
URL url = new URL(urlString);//urlString is a String parameter of the function
AtomicInteger elementCount = new AtomicInteger();//total count of all the different characters
Map<String, Integer> frequencies = new TreeMap<>();//where all the frequencies of the characters will be stored
//example: e=10, r=4, (=3 g=4...
//read and count all the characters, works fine
try (Stream<String> stream = new BufferedReader(
new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)).lines()) {
stream
.flatMapToInt(CharSequence::chars)
.filter(c -> !Character.isWhitespace(c))
.mapToObj(Character::toString)
.map(String::toLowerCase)
.forEach(s -> {
frequencies.merge(s, 1, Integer::sum);
elementCount.getAndIncrement();
});
} catch (IOException e) {
return "IOException:\n" + e.getMessage();
}
//counting the letters which are present the least amount of times
//in the example from above those are
//r=4, g=4
try (Stream<Map.Entry<String, Integer>> stream = frequencies.entrySet().stream()) {
Map<String, Integer> minEntryes = new TreeMap<>();
stream
.collect(Collectors.groupingBy(Map.Entry::getValue))
.entrySet()
.stream()
.min(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.ifPresent(key -> {
IntStream i = IntStream.rangeClosed(0, key.size());
i.forEach(s -> minEntryes.put(key.get(s).getKey(), key.get(s).getValue()));
});
wordFrequencies.append("\n\nSeltenste Zeichen: (").append(100 / elementCount.floatValue() * minEntryes.getValue().append("%)"));
//this does not work
minEntryes.forEach((key, value) -> wordFrequencies.append("\n'").append(key).append("'"));
}
The compiler tells me to call get(String key) but I don't know the key. So my code to get it into the Map is way to complicated, I know, but I can't use Optional in this case(the task prohibits it). I tried to do it more simple but nothing worked.
I could get a key from minEntryes.forEach, but im wondering if there's a better solution for this.
It's not clear to me what you are trying to do, but if the question is how to get the value without knowing the key:
1st method: Use an for loop
for (int value : minEntryes.values()) {
// use 'value' instead of 'minEntryes.getValue()'
}
2nd method: Iterator "hack" (If you know there is always one value)
int value = minEntryes.values().iterator().next();
// use 'value' instead of 'minEntryes.getValue()'

Refactor two for's into java 8 streams

I'm facing a small problem to rewrite my two for's into java 8 streams.
// This is a method parameter
Map<String, Collection<String>> preSelectedValues;
List<PersonModel> parameters = parameterSearchService.getParameterNames();
for(Iterator<Map.Entry<String, Collection<String>>> it = preSelectedValues.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Collection<String>> entry = it.next();
for(int i = 0; i < parameters.size(); i++) {
if (entry.getKey().startsWith(parameters.get(i).getName())) {
it.remove();
}
}
}
I've tried following streams to have the same behaviour as before:
Map<String, Collection<String>> filteredParameters = preSelectedValues.keySet().stream()
.filter(x -> isParameterValid(x, parameters))
.collect(Collectors.toMap(k -> k, v -> preSelectedValues.get(v)));
isParameterValid method:
private boolean isParameterValid(String parameterId, List<PersonModel> preSelectedValues) {
return preSelectedValues.stream()
.anyMatch(v -> !v.getName().startsWith(parameterId));
}
Basically what I'm trying to do is filter the "preSelectedValues" map which starts with "parameterId". But somehow when I'm using streams either it filters everything or nothing.
Your isParameterValid method doesn't have the same logic as the original loops, since:
You switched the instance and argument in the call to startsWith.
Calling anyMatch with a !v.getName().startsWith(parameterId) only tells you whether at least one element of the List<PersonModel> doesn't start with parameterId. Your original condition for keeping the entry in the Map was that all the elements of List<PersonModel> don't start with parameterId (or actually, the other way around - parameterId doesn't start with any of the names of elements of List<PersonModel>).
Therefore I negated the method to return the condition for removing an entry from the Map:
private boolean isParameterInvalid(String parameterId, List<PersonModel> preSelectedValues) {
return preSelectedValues.stream()
.anyMatch(v -> parameterId.startsWith(v.getName()));
}
And then the stream pipeline can look like this:
Map<String, Collection<String>> filteredParameters = preSelectedValues.entrySet().stream()
.filter(x -> !isParameterInvalid(x.getKey(), parameters))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
BTW, since your original loops mutate the original Map, you can achieve the same with removeIf.
If you are trying to modify the original map you can use removeIf:
preSelectedValues.keySet().removeIf(
key -> parameters.stream()
.map(PersonModel::getName)
.anyMatch(key::startsWith)
);

how to write a foreach with more if/else conditions in java8?

I'am trying to develop a method in java 8 with some foreach and more than one if condition.
I don't khnow if i can write for any if condition a filter? and how to do that in java 8?
Here is my methode:
public Map<LocalDate,boolean[]> getForkedDetails() {
TreeMap<LocalDate,boolean[]> map=new TreeMap<>();
this.getDetails().forEach(dept->{
boolean[] myArray=new boolean[3];
if(dept.getTypemt().equals(TypeMt.SIP)) {
myArray[0]=Boolean.TRUE;
}
if(dept.getTypemt().equals(TypeMt.DOP)) {
myArray[1]=Boolean.TRUE;
}
if(dept.getTypemt().equals(TypeMt.OPA) ) {
myArray[2]=Boolean.TRUE;
}
if(map.containsKey(dept.getDateFa())){
boolean[] bs = map.get(dept.getDateFa());
for(int index=0;index<bs.length;index++){
if(myArray[index]){
bs[index]=myArray[index];
}
}
map.put(dept.getDateFa(), bs);
}else{
map.put(dept.getDateFa(), myArray);
}
});
// get all dates between start and end dates
List<LocalDate> dates = Stream.iterate(this.getDateDebut(), date -> date.plusDays(1))
.limit(ChronoUnit.DAYS.between(this.getDateDebut(), this.getDateFin()))
.collect(Collectors.toList());
dates.forEach(date->{
if(!map.containsKey(date)){
map.put(date, new boolean[3]);
}
});
// Sorted TreeMap
TreeMap<LocalDate,boolean[]> result = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, TreeMap::new));
return result;
}
Before redesigning this further, there are a lot of things to clean up. First, the baroque array creation, then, using containsKey followed by get or put bears several unnecessary map lookups. You can use merge instead. Then, there is no need to collect a stream into a List, just to apply forEach on it. You can use forEach on the stream in the first place. Well, and TreeMaps are always sorted by key, there is no sense in performing a stream operation to sort it, just to collect into a TreeMap that will sort the entries by itself.
public Map<LocalDate,boolean[]> getForkedDetails() {
TreeMap<LocalDate,boolean[]> map=new TreeMap<>();
this.getDetails().forEach(dept -> {
boolean[] myArray= { dept.getTypemt().equals(TypeMt.SIP),
dept.getTypemt().equals(TypeMt.DOP),
dept.getTypemt().equals(TypeMt.OPA) };
map.merge(dept.getDateFa(), myArray, (bs,newArray) -> {
for(int index=0;index<bs.length;index++){
if(newArray[index]) bs[index]=true;
}
return bs;
});
});
// add entries for all missing dates between start and end dates
Stream.iterate(this.getDateDebut(), date -> date.plusDays(1))
.limit(ChronoUnit.DAYS.between(this.getDateDebut(), this.getDateFin()))
.forEach(date-> map.computeIfAbsent(date, key -> new boolean[3]));
// the TreeMap is already sorted
return map;
}
Then, the first part can be rewritten as
TreeMap<LocalDate,boolean[]> map = this.getDetails()
.stream()
.collect(Collectors.toMap(
dept -> dept.getDateFa(),
dept -> new boolean[] { dept.getTypemt().equals(TypeMt.SIP),
dept.getTypemt().equals(TypeMt.DOP),
dept.getTypemt().equals(TypeMt.OPA) },
(bs,newArray) -> {
for(int index=0;index<bs.length;index++){
if(newArray[index]) bs[index]=true;
}
return bs;
},
TreeMap::new));
Going off Lino's comment should be a way to go. Instead of all the spam with if-elseif-else just do what your condition is. It returns a boolean I assume so you should be able to just do that.
Your answer basically is don't use them because you don't need them. Short and sweet code is the best code (unless you then can't understand it code golfers)

Java 8 lambda get and remove element from list

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;
}

Use Streams to return Boolean if all the List values in a Map are empty/not-empty

Given a Map mapping a String to a List, is there a way to use Java Streams to return a Boolean where TRUE means one or more list had elements? If all lists in the map were empty, return FALSE.
Map< String , List<String> > map = …
Can use of Streams replace this conventional code?
// See if any diffs were found. Loop through the Map, look at each List of diffs to see if non-empty.
boolean anyElementsInAnyList = false;
for (List<String> list : map.values () ) {
if (!list.isEmpty()) {
anyElementsInAnyList = true;
break;
}
}
Note that we can break out of the examination after the first finding. No need to examine all the Map values (all the Lists). Would be nice if, for efficiency, the Stream could do the same stop-work-on-first-finding (a “short-circuiting” operation).
Stream::allMatch
In Java 8 you can check that not all lists are empty.
boolean anyNonEmpty = !map.values().stream().allMatch(List::isEmpty);
Notice that Stream::allMatch is a short-circuiting terminal operation. So the stream is efficient, not running any longer than need be.
Use the anyMatch method that finds if any element of the stream matches a Predicate. Here, your predicate is that the entry's value (the list) is not empty.
boolean anyNonEmpty = map.entrySet()
.stream()
.anyMatch(entry -> !entry.getValue().isEmpty());
int size = Map.entrySet().stream()
.map(entry -> entry.getValue())
.flatMap(list -> list.stream())
.size();
if(size==0)
return Boolean.False;
else
return Boolean.True;
This code is a simple one this may help for your work .

Categories