What is the best, cheapest, performantest way to achive the following:
I do have a Collection of Objects myObject, which provide a method returning a Set of Integers. I want to add all the items inside the sets into a new set.
LinkedList<myObject> ll = new LinkedList<>();
//fill the list bla bla
Set<Integer> result = ll.stream()
.map(f -> f.getTheSet())
.flatMap(Set::stream)
.collect(Collectors.toCollection(TreeSet::new));
System.out.println(result.toString());
is there a better way of getting a resulting set containing all integers from all objects?
I would like to avoid "unpacking the set" with the flatMap command. Instead i think about something like .addAll or does it in the end not matter, because .addAll unpacks anyway?
You can collect using addAll with a 3-argument collect:
Set<Integer> result = ll.stream()
.map(MyObject::getTheSet)
.collect(HashSet::new, Set::addAll, Set::addAll);
HashSet::new makes a new container, Set::addAll adds each set into the container, 2nd Set::addAll merges two containers if this stream were to be run in parallel.
(or use TreeSet::new if you specifically want a TreeSet)
Instead i think about something like .addAll or does it in the end not matter, because .addAll unpacks anyway?
TreeSet.addAll() has an optimized codepath for adding Collections that implement SortedSet assuming they have the same sort order.
Caveat: This is an undocumented implementation detail and thus subject to change.
You could easily have found this information yourself simply looking at the JDK source code, which is included in the src.zip shipped with the JDK.
Related
I know a set has no duplicates but the issue is that I can't add elements to it while iterating over it using an iterator or for each loop. Is there any other way? Thank you.
The ConcurrentHashMap class can be used for this. For example:
Set<T> set = Collections.newSetFromMap(new ConcurrentHashMap<T, Boolean>());
(You can replace <T, Boolean> with <> and let the compiler infer the types. I wrote it as above for illustrative purposes.)
The Collections::newSetFromMap javadoc says:
Returns a set backed by the specified map. The resulting set displays the same ordering, concurrency, and performance characteristics as the backing map. In essence, this factory method provides a Set implementation corresponding to any Map implementation.
Since ConcurrentHashMap allows simultaneous iteration and updates, so does the Set produced as above. The catch is that an iteration may not see the effect of additions or removals made while iterating.
The concurrency properties of iteration can be inferred from the javadoc for ConcurrentHashMap.
Is there any other way.
It depends on your requirements, but there are potentially ways to avoid the problem. For example, you could:
copy the set before iterating it, OR
add the new element to another new set and add the existing elements to the new set to the new set after ... or while ... iterating.
However, these these are unlikely to work without a concurrency bottleneck (e.g. 1.) or a differences in behavior (e.g. 2.)
Not sure whether below approach fixes your problem but you can try it:
HashSet<Integer> original = new HashSet<>();
HashSet<Integer> elementsToAdd = new HashSet<>();
elementsToAdd.add(element); //while iterating original
original.addAll(elementsToAdd); //Once done with iterating, just add all.
I've a SortedSet of TreeSet implementation, which I often convert to list via new ArrayList<>(mySortedSet) and then do subList(myList, 3) or any other number of elements.
How can I avoid using the subList on the new list creating and only get the ArrayList of required elements from SortedSet in one go.
Using Java 11 with Guava.
Using Java only:
List<E> list = sortedSet.stream().limit(3).collect(Collectors.toList());
ImmutableList.copyOf(Iterables.limit(sortedSet, 3));
That should be all you need -- Iterables.limit does the important part.
For three smallest elements you could use
Iterator<E> it = mySortedSet.iterator();
ArrayList<E> = Lists.newArrayList(it.next(), it.next(), it.next());
This looks pretty hacky as it depends on the evaluation order of the arguments (which is guaranteed in Java). It also blows when the set is smaller. I'm not really recommending it as I wrote it, but you get the idea.
However, when all you need is these three elements, you can use a PriorityQueue or Guava's Comparators.least(int, Comparator).
Do I understand this correctly? Is this how Java developers do it? Or is there a better way?
So if I want to find a key in a map that matches a predicate, I must first get the key set from the map through the conveniently supplied method. THEN, I have to convert the set into a stream through the conveniently supplied method. THEN, I have to filter the stream with my predicate through the conveniently supplied method. THEN, I have to convert the stream into a container of a type of my choosing, possibly supplying a collector to do so, through the conveniently supplied method. THEN, I can at least check the container for empty to know if anything matched. Only then can I use the key(s) to extract the values of interest, or I could have used the entry set from the beginning and spare myself the extra step.
Is this the way, really? Because as far as I can tell, there are no other methods either built into the map or provided as a generic search algorithm over iterators or some other container abstraction.
I prefer entrySet myself as well. You should find this efficient:
Map<String, Integer> map; //Some example Map
//The map is filled here
List<Integer> valuesOfInterest = map.entrySet()
.stream() //Or parallelStream for big maps
.filter(e -> e.getKey().startsWith("word")) //Or some predicate
.map(Map.Entry::getValue) //Get the values
.collect(Collectors.toList()); //Put them in a list
The list is empty if nothing matched. This is useful if multiple keys match the predicate.
In a nutshell, it is as simple as:
Predicate<T> predicate = (t -> <your predicate here>);
return myMap.keySet()
.stream()
.filter(predicate)
.findAny()
.map(myMap::get);
returns an empty Optional if no key matches
(nota: findAny is better than findFirst because it does not prevent parallelization if relevant, and findFirst is useless anyway since the Set of keys is not sorted in any meaningful way, unless your Map is a SortedMap)
It’s not clear why you are shouting “THEN” so often. It’s the standard way of solving problems, to combine tools designed for broad variety of use cases to get your specific result. There is a built-in capability for traversing a sequence of elements and search for matches, the Stream API. Further, the Map interface provides you with the Collection views, keySet(), entrySet(), and values(), to be able to use arbitrary tools operating on Collections, the bridge to the Stream API being one of them.
So if you have a Map<Key,Value> and are interested in the values, whose keys match a predicate, you may use
List<Value> valuesOfInterest = map.entrySet().stream()
.filter(e -> e.getKey().fulfillsCondition())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
which consists of three main steps, filter to select matches, map to specify whether you are interested in the key, value, entry or a converted value of each matche and collect(Collectors.toList()) to specify that you want to collect the results into a List.
Each of these steps could be replaced by a different choice and the entire stream pipeline could be augmented by additional processing steps. Since you want this specific combination of operations, there is nothing wrong with having to specify exactly these three steps instead of getting a convenience method for your specific use case.
The initial step of entrySet().stream() is required as you have to select the entry set as starting point and switch to the Stream API which is the dedicated API for element processing that doesn’t modify the source. The Collection API, on the other hand, provides you with methods with might mutate the source. If you are willing to use that, the alternative to the code above is
map.keySet().removeIf(key -> !key.fulfillsCondition());
Collection<Value> valuesOfInterest=map.values();
which differs in that the nonmatching entries are indeed removed from the source map. Surely, you don’t want to confuse these two, so it should be understandable, why there is a clear separation between the Collection API and the Stream API.
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.
Is there a reason there's
Lists.transform()
but no
Lists.filter()
?
How do I filter a list correctly? I could use
new ArrayList(Collection2.filter())
of course, but this way it's not guaranteed that my ordering stays the same, if I understand correctly.
It wasn't implemented because it would expose a perilous large number of slow methods, such as #get(index) on the returned List view (inviting performance bugs). And ListIterator would be a pain to implement as well (though I submitted a patch years ago to cover that).
Since indexed methods can't be efficient in the filtered List view, it's better to just go with a filtered Iterable, which doesn't have them.
You can use Iterables.filter, which will definitely maintain ordering.
Note that by constructing a new list, you'll be copying the elements (just references, of course) - so it won't be a live view onto the original list. Creating a view would be pretty tricky - consider this situation:
Predicate<StringBuilder> predicate =
/* predicate returning whether the builder is empty */
List<StringBuilder> builders = Lists.newArrayList();
List<StringBuilder> view = Lists.filter(builders, predicate);
for (int i = 0; i < 10000; i++) {
builders.add(new StringBuilder());
}
builders.get(8000).append("bar");
StringBuilder firstNonEmpty = view.get(0);
That would have to iterate over the whole original list, applying the filter to everything. I suppose it could require that the predicate matching didn't change over the lifetime of the view, but that would be not-entirely-satisfactory.
(This is just guessing, mind you. Maybe one of the Guava maintainers will chip in with the real reason :)
I could use new List(Collection2.filter()) of course, but this way it's not guaranteed that my ordering stays the same.
This isn't true. Collections2.filter() is a lazily-evaluated function - it doesn't actually filter your collection until you start accessing the filtered version. For example, if you iterate over the filtered version, then the filtered elements will pop out of the iterator in the same order as your original collection (minus the ones filtered out, obviously).
Perhaps you were thinking that it does the filtering up front, then dumps the results into an arbitrary, unordered Collection of some form - it doesn't.
So if you use the output of Collections2.filter() as the input to a new list, then your original order will be retained.
Using static imports (and the Lists.newArrayList function), it becomes fairly succinct:
List filteredList = newArrayList(filter(originalList, predicate));
Note that while Collections2.filter will not eagerly iterate over the underlying collection, Lists.newArrayList will - it will extract all elements of the filtered collection and copy them into a new ArrayList.
As mentioned by Jon, you can use Iterables.filter(..) or Collections2.filter(..) and if you don't need a live view you can use ImmutableList.copyOf(Iterables.filter(..)) or Lists.newArrayList( Iterables.filter(..)) and yes ordering will be maintained.
If you are really interested in why part, you can visit https://github.com/google/guava/issues/505 for more details.
Summing up what the others said, you can easily create a generic wrapper to filter lists:
public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
return Lists.newArrayList(Iterables.filter(userLists, predicate));
}