I need an assistance on one of the optional concepts of java 8+. I have been seeing developers using Optional.ofNullable on list object which is incorrect and I am not sure how its been working perfectly without bugs/defect. Code goes this way
Note: list object is being captured by making DB call from repository layer.
Optional.ofNullable(list) .orElse(Collections.emptyList()) .stream().forEach(x-> { ------ ------};);
List cannot be literal null if there is no elements it would be empty([]) so, how can it handle Optional.ofNullable() correctly? I tried dummy code to test by adding print statement in forEach It would print in both cases(list empty as well not empty).
If the list is null, which it could be depending on use case, it creates an empty list and prints its contents which will be empty (but no exception will be thrown). If it is not empty, it will print its contents.
List<String> list = null;
// prints empty list
Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
.forEach(System.out::println);
list = List.of("A", "B", "C");
// print the contents.
Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
.forEach(System.out::println);
Prints
A
B
C
I presume the idea is to handle a null list by processing an empty Collection.
Related
Given the following code:
List<String> strList = new ArrayList<>(Arrays.asList("Java","Python","Php"));
Stream<String> jFilter = strList.stream().filter(str -> str.startsWith("J"));
strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation
System.out.println(Arrays.toString(jFilter.toArray()));
which outputs:
[Java, JavaScript, JQuery]
Why do JavaScript and JQuery appear in the filtered result even though they were added after creating the filtered stream?
Short Answer
You're assuming after this point:
Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));
That a new stream of the elements starting with "J" are returned i.e. only Java. However this is not the case;
streams are lazy i.e. they don't perform any logic unless told otherwise by a terminal operation.
The actual execution of the stream pipeline starts on the toArray() call and since the list was modified before the terminal toArray() operation commenced the result will be [Java, JavaScript, JQuery].
Longer Answer
here's part of the documentation which mentions this:
For well-behaved stream sources, the source can be modified before
the terminal operation commences and those modifications will be
reflected in the covered elements. For example, consider the following
code:
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
String s = sl.collect(joining(" "));
First a list is created consisting of two strings: "one"; and "two". Then a stream is created
from that list. Next the list is modified by adding a third string:
"three". Finally the elements of the stream are collected and joined
together. Since the list was modified before the terminal collect
operation commenced the result will be a string of "one two three".
All the streams returned from JDK collections, and most other JDK
classes, are well-behaved in this manner;
Until the statement
System.out.println(Arrays.toString(jFilter.toArray()));
runs, the stream doesn't do anything. A terminal operation (toArray in the example) is required for the stream to be traversed and your intermediate operations (filter in this case) to be executed.
In this case, what you can do is, for example, capture the size of the list before adding other elements:
int maxSize = strList.size();
Stream<String> jFilter = strStream.limit(maxSize)
.filter(str -> str.startsWith("J"));
where limit(maxSize) will not allow more than the initial elements to go through the pipeline.
Its because the stream never got evaluated. you never called a "Terminal operation" on that stream for it to get executed as they're lazy.
Look at a modification of your code and the output. The filtering actually takes place when you call the Terminal Operator.
public static void main(String []args){
List<String> strList = new ArrayList<>();
strList.add("Java");
strList.add("Python");
strList.add("Php");
Stream<String> strStream = strList.stream();
Stream<String> jFilter = strStream.filter(str -> {
System.out.println("Filtering" + str);
return str.startsWith("J");
});
System.out.println("After Stream creation");
strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation
System.out.println(Arrays.toString(jFilter.toArray()));
}
Output:
After Stream creation
FilteringJava
FilteringPython
FilteringPhp
FilteringJavaScript
FilteringJQuery
[Java, JavaScript, JQuery]
As explained in the official documentation at ,https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html, streams have no storage, and so are more like iterators than collections, and are evaluated lazily.
So, nothing really happens with respect to the stream until you invoke the terminal operation toArray()
#Hadi J's comment but it should be answer according to the rules.
Because streams are lazy and when you call terminal operation it executed.
The toArray method is the terminal operation and it works on that full content of your list. To get predictable result do not save the stream to a temporary variable as it will lead to misleading results. A better code is:
String[] arr = strList.stream().filter(str -> str.startsWith("J")).toArray();
I have DTO which contains several attributes on which is of type ArrayList.
I want take this DTO as parameter.
Then get the attribute with ArrayList.
Then based some predicate I want to remove some elements from the ArrayList
Finally return the modified DTO
public class SomeDTO{
private String attrOne;
private String attrTwo;
private List<SomeOtherDataType> listAttr;
// getters
// setter
...
}
// This is the method where I want to modify the DTO
private void modifyDTO(SomeDTO someDTO){
someDTO.getlistAttr()
.stream()
/// need help here, how to remove some element from list
/// based on some condition.
/// note the changes in the list should be reflected in DTO
}
This can be done by simply doing a forEach terminal operation but is there a better way to this or any best practise that others follow.
Thanks
in fact you have two ways :
collecting a new List with the elements to keep and assign it to the field
removing element from the actual List
The second way is probably what you need for multiple reasons such :
generally for a List field you don't want to create a new List to change its state. You want to that only for fields that refer to immutable objects.
if some objects keep a reference on the List field, these objects will refer the old object. Which is not desirable and can create side effect issues hard to understand.
creating the whole filtered objects may have a cost in terms of CPU/memory while removing objects from the existing List is very probably more efficient.
1 way) Reassign to a new object
List<SomeOtherDataType> listAttr =
someDTO.getlistAttr()
.stream()
.filter(o -> conditionToKeep)
.collect(Collectors.toList());
someDTO.setListAttr(listAttr);
2 way) Filter in the current object
someDTO.getlistAttr().removeIf(o -> conditionToRemove);
Edit about the OP comment
Even if the condition to filter out element requires to dig on the SomeOtherDataType object and the elements that compose it , the stream is still not required.
A fancy code to illustrate that :
someDTO.getlistAttr()
.removeIf(o -> {
Bar bar = o.getFoo().getBar();
if (bar != null){
List<FooBar> fooBars = bar.getList();
if (fooBars.contains(...)){
return true; // I remove in this case
}
}
return false; // in any other case I keep
}
);
There is no need to use streams here. You can simply use Collection.removeIf.
From the javadoc (emphasis mine)
Removes all of the elements of this collection that satisfy the given predicate. Errors or runtime exceptions thrown during iteration or by the predicate are relayed to the caller.
Hence, you can write
someDTO.getlistAttr().removeIf((someOtherDataType) -> /*Your logic here*/)
If you really want to use Streams, then from where you have left, you can use a filter and collect the SomeOtherDataType elements that pass the filter as a list.
List<SomeOtherDataType> filterdList = someDTO.getlistAttr()
.stream()
.filter(someOtherDataType -> /* your logic */)
.collect(Collectors.toList())
But after this, you would either have to
Create a new SomeDTO with the above filterdList (new SomeDTO(someDTO.getAttrOne(), someDTO.getAttrTwo(), filterdList))1 or
Set the existing listAttr with filterdList (someDTO.setListAttr(filterdList))
1 I think you meant the name of the second instance variable to be attrTwo.
today i dealt with a Java problem that really confused me.
I have the following code:
List<ObjectXY> someList = obj.getListOfObjectsXY(); // getter returns 2 elements
someList.add(new ObjectXY());
obj.getListOfObjectsXY(); // getter now returns 3 elements
When i add an element to a list, the getter gets some kind of overwritten. Is this because someList acts like a reference on the result of the getter in this case? Or what else causes this effect?
I solved the problem with the following code by using another list:
List<ObjectXY> someList = obj.getListOfObjectsXY(); // result: 2 elements
List<ObjectXY> someOtherList = new ArrayList<ObjectXY>();
someOtherList.addAll(someList);
someOtherList.add(new ObjectXY());
obj.getListOfObjectsXY(); // result: 2 elements
But i am still some kind of confused because i didn't expect Java to behave this way.
Can anyone explain to me what i did wrong and why it is so?
Thanks in advance!
The returned result is indeed just a copy of a reference to the same object as you are using internally. Counting on the caller to not modify the object is error-prone.
One solution is to return a reference to an unmodifiable list wrapping your list. See Collections.unmodifiableList(). The getter caller will be unable to modify your list.
Is this because someList acts like a reference on the result of the
getter in this case?
Yes. The list you received was just a reference to the same, original list you had. Any changes made on this variable would be reflected on the original list.
By adding the list's values to a new list you explicitly constructed a new object and thus they are separated.
In your case, obj.getListOfObjectsXY() everytime return you the same object and in Java object references are pass-by-value. So, when you do a someList.add(new ObjectXY());, it's actually setting the property of the object someList which is poiting to obj.getListOfObjectsXY().
And in the latter case, you are just copying someList to someOtherList. Then you added one more element to the same someOtherList but not to the someList. So, you getting 2 elements in
obj.getListOfObjectsXY(); is perfectly valid.
I have a List<SomeBean> that is populated from a Web Service. I want to copy/clone the contents of that list into an empty list of the same type. A Google search for copying a list suggested me to use Collections.copy() method. In all the examples I saw, the destination list was supposed to contain the exact number of items for the copying to take place.
As the list I am using is populated through a web service and it contains hundreds of objects, I cannot use the above technique. Or I am using it wrong??!! Anyways, to make it work, I tried to do something like this, but I still got an IndexOutOfBoundsException.
List<SomeBean> wsList = app.allInOne(template);
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList.size());
Collections.copy(wsListCopy,wsList);
System.out.println(wsListCopy.size());
I tried to use the wsListCopy=wsList.subList(0, wsList.size()) but I got a ConcurrentAccessException later in the code. Hit and trial. :)
Anyways, my question is simple, how can I copy the entire content of my list into another List? Not through iteration, of course.
Just use this:
List<SomeBean> newList = new ArrayList<SomeBean>(otherList);
Note: still not thread safe, if you modify otherList from another thread, then you may want to make that otherList (and even newList) a CopyOnWriteArrayList, for instance -- or use a lock primitive, such as ReentrantReadWriteLock to serialize read/write access to whatever lists are concurrently accessed.
This is a really nice Java 8 way to do it:
List<String> list2 = list1.stream().collect(Collectors.toList());
Of course the advantage here is that you can filter and skip to only copy of part of the list.
e.g.
//don't copy the first element
List<String> list2 = list1.stream().skip(1).collect(Collectors.toList());
originalArrayList.addAll(copyArrayofList);
Please keep on mind whenever using the addAll() method for copy, the contents of both the array lists (originalArrayList and copyArrayofList) references to the same objects will be added to the list so if you modify any one of them then copyArrayofList also will also reflect the same change.
If you don't want side effect then you need to copy each of element from the originalArrayList to the copyArrayofList, like using a for or while loop. for deep copy you can use below code snippet.
but one more thing you need to do, implement the Cloneable interface and override the clone() method for SomeBean class.
public static List<SomeBean> cloneList(List<SomeBean> originalArrayList) {
List<SomeBean> copyArrayofList = new ArrayList<SomeBean>(list.size());
for (SomeBean item : list) copyArrayofList.add(item.clone());
return copyArrayofList;
}
I tried to do something like this, but I still got an IndexOutOfBoundsException.
I got a ConcurrentAccessException
This means you are modifying the list while you are trying to copy it, most likely in another thread. To fix this you have to either
use a collection which is designed for concurrent access.
lock the collection appropriately so you can iterate over it (or allow you to call a method which does this for you)
find a away to avoid needing to copy the original list.
Starting from Java 10:
List<E> oldList = List.of();
List<E> newList = List.copyOf(oldList);
List.copyOf() returns an unmodifiable List containing the elements of the given Collection.
The given Collection must not be null, and it must not contain any null elements.
Also, if you want to create a deep copy of a List, you can find many good answers here.
There is another method with Java 8 in a null-safe way.
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.collect(Collectors.toList());
If you want to skip one element.
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.skip(1)
.collect(Collectors.toList());
With Java 9+, the stream method of Optional can be used
Optional.ofNullable(wsList)
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList())
I tried something similar and was able to reproduce the problem (IndexOutOfBoundsException). Below are my findings:
1) The implementation of the Collections.copy(destList, sourceList) first checks the size of the destination list by calling the size() method. Since the call to the size() method will always return the number of elements in the list (0 in this case), the constructor ArrayList(capacity) ensures only the initial capacity of the backing array and this does not have any relation to the size of the list. Hence we always get IndexOutOfBoundsException.
2) A relatively simple way is to use the constructor that takes a collection as its argument:
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList);
I was having the same problem ConcurrentAccessException and mysolution was to:
List<SomeBean> tempList = new ArrayList<>();
for (CartItem item : prodList) {
tempList.add(item);
}
prodList.clear();
prodList = new ArrayList<>(tempList);
So it works only one operation at the time and avoids the Exeption...
You can use addAll().
eg : wsListCopy.addAll(wsList);
re: indexOutOfBoundsException, your sublist args are the problem; you need to end the sublist at size-1. Being zero-based, the last element of a list is always size-1, there is no element in the size position, hence the error.
I can't see any correct answer. If you want a deep copy you have to iterate and copy object manually (you could use a copy constructor).
You should use the addAll method. It appends all of the elements in the specified collection to the end of the copy list. It will be a copy of your list.
List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
List<String> copyList = new ArrayList<>();
copyList.addAll(myList);
just in case you use Lombok:
mark SomeBean with the following annotation:
#Builder(toBuilder = true, builderMethodName = "")
and Lombok will perform a shallow copy of objects for you using copy constructor:
inputList.stream()
.map(x -> x.toBuilder().build())
.collect(Collectors.toList());
subList function is a trick, the returned object is still in the original list.
so if you do any operation in subList, it will cause the concurrent exception in your code, no matter it is single thread or multi thread.
I start learning the Java generic collection using Deitel Harvey book - but I am facing a difficulty understanding the three line of codes below - Do all of them perform the same operation on by intializing and adding the relevant values of array ( colors ) to the LinkList variable (list1). How does the second method and third method works - I am having a bit difficulty understanding how Arrays can viewed as a list.. As I know arrays are not dynamic data structure, they have fixed sized length, adding/ removing elements on array can not be done on running time comparing to Lists in general.
String[] colors = { "black", "white", "blue", "cyan" };
List< String > list1 = new LinkedList< String >();
// method 1 of initalizing and adding elments to the list
for (String color : colors)
list1.add(color);
// method 2 of initializing and adding elements to the list
List< String > list1 = new LinkedList< String > (Arrays.asList(colors));
// method 3 of initializing and adding elements to the list
List< String > list1 = Arrays.asList(colors);
Please help me understand my queries above, don't judge me as I am still new to this.
Thank you, Sinan
Actually knowledge of generics is not necessary for answering this question.
As you correctly identifier arrays are static in the sense that you can't add elements to them or remove them.
Lists, however, usually allow those operations.
The List returned by Arrays.asList() does have the add/remove methods (otherwise it would not be a valid List). However actually calling those methods will throw an UnsupportedOperationException exactly because you can't actually add elements to an array (for which this List is simply a view/wrapper).
Operations that don't structurally modify the list (i.e. that don't change the number of elements in the list) are entirely possible: set(int, E) works just fine on the List returned by Arrays.asList().
Arrays.asList returns a fixed-size list backed by the specified array.
It is actually a bridge between Array and Collection framework. But returned list write through to the array.
Only your first method does anything to the LinkedList you have initially assigned into list1. The other two assign a new, unrelated list to it. The third option assigns something that isn't a LinkedList, but a special implementation of the List interface backed by your String array. In the third case you won't be able to add/remove elements from the list, but you can iterate over it and update existing slots. Basically, it does what a plain array does, just through the List interface.
Arrays.asList creates a List from an Array. Arrays in general can't be viewed as lists in Java. They can only be wrapped in a list.
So method 2 is used to have a specific list implementation LinkedList in this case.
to Method 2, just check the Api here:
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/LinkedList.html#LinkedList(java.util.Collection)
For sure, Lists implement the Collections Interface so this Constructor will work here.
to Method 3, just check out the Api here: http://docs.oracle.com/javase/6/docs/api/java/util/Arrays.html#asList(T...)
Every time you are interested in implementation you can look into certain method. For example, by press Ctrl+left mouse button onto method or class.
// method 2 of initializing and adding elements to the list
List<String> list1 = new LinkedList<String> (Arrays.asList(colors));
This code leads to:
List<String> list1 = new LinkedList<String> (new ArrayList<String>(colors));
In constructor of ArrayList:
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
the actual array is copied to encapsulated private array field(link is copied).
Then in constructor of LinkedList:
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
Every element of passed collection is added to the LinkedList.
if you see the link below
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/LinkedList.html#LinkedList%28java.util.Collection%29
you will see the constructor of linked list class which is accepting a collection object as parameter.
Any in your post, the 2nd and 3 rd lines are passing an object of collection class(i.e Arrays.asList is finally giving a List which is a sub class of collection).
So both 2nd and 3rd lines fairly valid implementations.
More over you can observe one more good coding practice in all the 3 lines.
That is
writing code to interceptors than to classes
. (referring
LinkedList
instance with
List
interface)
Always try to refer your classes with interceptors which is a good practice