Are there any methods to do so? I was looking but couldn't find any.
Another question: I need these methods so I can filter files.
Some are AND filters and some are OR filters (like in set theory), so I need to filter according to all files and the unite/intersects ArrayLists that holds those files.
Should I use a different data structure to hold the files? Is there anything else that would offer a better runtime?
Here's a plain implementation without using any third-party library. Main advantage over retainAll, removeAll and addAll is that these methods don't modify the original lists input to the methods.
public class Test {
public static void main(String... args) throws Exception {
List<String> list1 = new ArrayList<String>(Arrays.asList("A", "B", "C"));
List<String> list2 = new ArrayList<String>(Arrays.asList("B", "C", "D", "E", "F"));
System.out.println(new Test().intersection(list1, list2));
System.out.println(new Test().union(list1, list2));
}
public <T> List<T> union(List<T> list1, List<T> list2) {
Set<T> set = new HashSet<T>();
set.addAll(list1);
set.addAll(list2);
return new ArrayList<T>(set);
}
public <T> List<T> intersection(List<T> list1, List<T> list2) {
List<T> list = new ArrayList<T>();
for (T t : list1) {
if(list2.contains(t)) {
list.add(t);
}
}
return list;
}
}
Collection (so ArrayList also) have:
col.retainAll(otherCol) // for intersection
col.addAll(otherCol) // for union
Use a List implementation if you accept repetitions, a Set implementation if you don't:
Collection<String> col1 = new ArrayList<String>(); // {a, b, c}
// Collection<String> col1 = new TreeSet<String>();
col1.add("a");
col1.add("b");
col1.add("c");
Collection<String> col2 = new ArrayList<String>(); // {b, c, d, e}
// Collection<String> col2 = new TreeSet<String>();
col2.add("b");
col2.add("c");
col2.add("d");
col2.add("e");
col1.addAll(col2);
System.out.println(col1);
//output for ArrayList: [a, b, c, b, c, d, e]
//output for TreeSet: [a, b, c, d, e]
This post is fairly old, but nevertheless it was the first one popping up on google when looking for that topic.
I want to give an update using Java 8 streams doing (basically) the same thing in a single line:
List<T> intersect = list1.stream()
.filter(list2::contains)
.collect(Collectors.toList());
List<T> union = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
If anyone has a better/faster solution let me know, but this solution is a nice one liner that can be easily included in a method without adding a unnecessary helper class/method and still keep the readability.
list1.retainAll(list2) - is intersection
union will be removeAll and then addAll.
Find more in the documentation of collection(ArrayList is a collection)
http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html
Unions and intersections defined only for sets, not lists. As you mentioned.
Check guava library for filters. Also guava provides real intersections and unions
static <E> Sets.SetView<E >union(Set<? extends E> set1, Set<? extends E> set2)
static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)
You can use CollectionUtils from apache commons.
The solution marked is not efficient. It has a O(n^2) time complexity. What we can do is to sort both lists, and the execute an intersection algorithm as the one below.
private static ArrayList<Integer> interesect(ArrayList<Integer> f, ArrayList<Integer> s) {
ArrayList<Integer> res = new ArrayList<Integer>();
int i = 0, j = 0;
while (i != f.size() && j != s.size()) {
if (f.get(i) < s.get(j)) {
i ++;
} else if (f.get(i) > s.get(j)) {
j ++;
} else {
res.add(f.get(i));
i ++; j ++;
}
}
return res;
}
This one has a complexity of O(n log n + n) which is in O(n log n).
The union is done in a similar manner. Just make sure you make the suitable modifications on the if-elseif-else statements.
You can also use iterators if you want (I know they are more efficient in C++, I dont know if this is true in Java as well).
One-liners since JAVA 8
Union
if there are no duplicates:
return concat(a.stream(), b.stream()).collect(toList());
union and distinct:
return concat(a.stream(), b.stream()).distinct().collect(toList());
union and distinct if Collection/Set return type:
return concat(a.stream(), b.stream()).collect(toSet());
Intersect
if no duplicates:
return a.stream().filter(b::contains).collect(toList());
PERFORMANCE: If collection b is huge and not O(1), then pre-optimize the filter performance by adding 1 line before return: Copy to HasSet (import java.util.Set;):
... b = Set.copyOf(b);
intersect and distinct:
return a.stream().distinct().filter(b::contains).collect(toList());
- imports
import static java.util.stream.Stream.concat;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
I think you should use a Set to hold the files if you want to do intersection and union on them. Then you can use Guava's Sets class to do union, intersection and filtering by a Predicate as well. The difference between these methods and the other suggestions is that all of these methods create lazy views of the union, intersection, etc. of the two sets. Apache Commons creates a new collection and copies data to it. retainAll changes one of your collections by removing elements from it.
Here is a way how you can do an intersection with streams (remember that you have to use java 8 for streams):
List<foo> fooList1 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<foo> fooList2 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
fooList1.stream().filter(f -> fooList2.contains(f)).collect(Collectors.toList());
An example for lists with different types. If you have a realtion between foo and bar and you can get a bar-object from foo than you can modify your stream:
List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));
fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());
You can use commons-collections4 CollectionUtils
Collection<Integer> collection1 = Arrays.asList(1, 2, 4, 5, 7, 8);
Collection<Integer> collection2 = Arrays.asList(2, 3, 4, 6, 8);
Collection<Integer> intersection = CollectionUtils.intersection(collection1, collection2);
System.out.println(intersection); // [2, 4, 8]
Collection<Integer> union = CollectionUtils.union(collection1, collection2);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]
Collection<Integer> subtract = CollectionUtils.subtract(collection1, collection2);
System.out.println(subtract); // [1, 5, 7]
retainAll will modify your list
Guava doesn't have APIs for List (only for set)
I found ListUtils very useful for this use case.
Use ListUtils from org.apache.commons.collections if you do not want to modify existing list.
ListUtils.intersection(list1, list2)
In Java 8, I use simple helper methods like this:
public static <T> Collection<T> getIntersection(Collection<T> coll1, Collection<T> coll2){
return Stream.concat(coll1.stream(), coll2.stream())
.filter(coll1::contains)
.filter(coll2::contains)
.collect(Collectors.toSet());
}
public static <T> Collection<T> getMinus(Collection<T> coll1, Collection<T> coll2){
return coll1.stream().filter(not(coll2::contains)).collect(Collectors.toSet());
}
public static <T> Predicate<T> not(Predicate<T> t) {
return t.negate();
}
If the objects in the list are hashable (i.e. have a decent hashCode and equals function), the fastest approach between tables approx. size > 20 is to construct a HashSet for the larger of the two lists.
public static <T> ArrayList<T> intersection(Collection<T> a, Collection<T> b) {
if (b.size() > a.size()) {
return intersection(b, a);
} else {
if (b.size() > 20 && !(a instanceof HashSet)) {
a = new HashSet(a);
}
ArrayList<T> result = new ArrayList();
for (T objb : b) {
if (a.contains(objb)) {
result.add(objb);
}
}
return result;
}
}
I was also working on the similar situation and reached here searching for help. Ended up finding my own solution for Arrays.
ArrayList AbsentDates = new ArrayList(); // Will Store Array1-Array2
Note : Posting this if it can help someone reaching this page for help.
ArrayList<String> AbsentDates = new ArrayList<String>();//This Array will store difference
public void AbsentDays() {
findDates("April", "2017");//Array one with dates in Month April 2017
findPresentDays();//Array two carrying some dates which are subset of Dates in Month April 2017
for (int i = 0; i < Dates.size(); i++) {
for (int j = 0; j < PresentDates.size(); j++) {
if (Dates.get(i).equals(PresentDates.get(j))) {
Dates.remove(i);
}
}
AbsentDates = Dates;
}
System.out.println(AbsentDates );
}
Intersection of two list of different object based on common key - Java 8
private List<User> intersection(List<User> users, List<OtherUser> list) {
return list.stream()
.flatMap(OtherUser -> users.stream()
.filter(user -> user.getId()
.equalsIgnoreCase(OtherUser.getId())))
.collect(Collectors.toList());
}
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
Set<T> set1, set2;
if (col1 instanceof Set) {
set1 = (Set) col1;
} else {
set1 = new HashSet<>(col1);
}
if (col2 instanceof Set) {
set2 = (Set) col2;
} else {
set2 = new HashSet<>(col2);
}
Set<T> intersection = new HashSet<>(Math.min(set1.size(), set2.size()));
for (T t : set1) {
if (set2.contains(t)) {
intersection.add(t);
}
}
return intersection;
}
JDK8+ (Probably Best Performance)
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
boolean isCol1Larger = col1.size() > col2.size();
Set<T> largerSet;
Collection<T> smallerCol;
if (isCol1Larger) {
if (col1 instanceof Set) {
largerSet = (Set<T>) col1;
} else {
largerSet = new HashSet<>(col1);
}
smallerCol = col2;
} else {
if (col2 instanceof Set) {
largerSet = (Set<T>) col2;
} else {
largerSet = new HashSet<>(col2);
}
smallerCol = col1;
}
return smallerCol.stream()
.filter(largerSet::contains)
.collect(Collectors.toSet());
}
If you don't care about performance and prefer smaller code just use:
col1.stream().filter(col2::contains).collect(Collectors.toList());
First, I am copying all values of arrays into a single array then I am removing duplicates values into the array. Line 12, explaining if same number occur more than time then put some extra garbage value into "j" position. At the end, traverse from start-end and check if same garbage value occur then discard.
public class Union {
public static void main(String[] args){
int arr1[]={1,3,3,2,4,2,3,3,5,2,1,99};
int arr2[]={1,3,2,1,3,2,4,6,3,4};
int arr3[]=new int[arr1.length+arr2.length];
for(int i=0;i<arr1.length;i++)
arr3[i]=arr1[i];
for(int i=0;i<arr2.length;i++)
arr3[arr1.length+i]=arr2[i];
System.out.println(Arrays.toString(arr3));
for(int i=0;i<arr3.length;i++)
{
for(int j=i+1;j<arr3.length;j++)
{
if(arr3[i]==arr3[j])
arr3[j]=99999999; //line 12
}
}
for(int i=0;i<arr3.length;i++)
{
if(arr3[i]!=99999999)
System.out.print(arr3[i]+" ");
}
}
}
After testing, here is my best intersection approach.
Faster speed compared to pure HashSet Approach. HashSet and HashMap below has similar performance for arrays with more than 1 million records.
As for Java 8 Stream approach, speed is quite slow for array size larger then 10k.
Hope this can help.
public static List<String> hashMapIntersection(List<String> target, List<String> support) {
List<String> r = new ArrayList<String>();
Map<String, Integer> map = new HashMap<String, Integer>();
for (String s : support) {
map.put(s, 0);
}
for (String s : target) {
if (map.containsKey(s)) {
r.add(s);
}
}
return r;
}
public static List<String> hashSetIntersection(List<String> a, List<String> b) {
Long start = System.currentTimeMillis();
List<String> r = new ArrayList<String>();
Set<String> set = new HashSet<String>(b);
for (String s : a) {
if (set.contains(s)) {
r.add(s);
}
}
print("intersection:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
return r;
}
public static void union(List<String> a, List<String> b) {
Long start = System.currentTimeMillis();
Set<String> r= new HashSet<String>(a);
r.addAll(b);
print("union:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
}
retainAll() method use for finding common element..i.e;intersection
list1.retainAll(list2)
You can use the methods:
CollectionUtils.containsAny and CollectionUtils.containsAll
from Apache Commons.
Final solution:
//all sorted items from both
public <T> List<T> getListReunion(List<T> list1, List<T> list2) {
Set<T> set = new HashSet<T>();
set.addAll(list1);
set.addAll(list2);
return new ArrayList<T>(set);
}
//common items from both
public <T> List<T> getListIntersection(List<T> list1, List<T> list2) {
list1.retainAll(list2);
return list1;
}
//common items from list1 not present in list2
public <T> List<T> getListDifference(List<T> list1, List<T> list2) {
list1.removeAll(list2);
return list1;
}
If you had your data in Sets you could use Guava's Sets class.
If the number matches than I am checking it's occur first time or not with help of "indexOf()" if the number matches first time then print and save into in a string so, that when the next time same number matches then it's won't print because due to "indexOf()" condition will be false.
class Intersection
{
public static void main(String[] args)
{
String s="";
int[] array1 = {1, 2, 5, 5, 8, 9, 7,2,3512451,4,4,5 ,10};
int[] array2 = {1, 0, 6, 15, 6, 5,4, 1,7, 0,5,4,5,2,3,8,5,3512451};
for (int i = 0; i < array1.length; i++)
{
for (int j = 0; j < array2.length; j++)
{
char c=(char)(array1[i]);
if(array1[i] == (array2[j])&&s.indexOf(c)==-1)
{
System.out.println("Common element is : "+(array1[i]));
s+=c;
}
}
}
}
}
Related
Conditions: do not modify the original lists; JDK only, no external libraries. Bonus points for a one-liner or a JDK 1.3 version.
Is there a simpler way than:
List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
In Java 8:
List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.toList());
Java 16+:
List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).toList();
Off the top of my head, I can shorten it by one line:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
You could use the Apache commons-collections library:
List<String> newList = ListUtils.union(list1, list2);
Another Java 8 one-liner:
List<String> newList = Stream.of(listOne, listTwo)
.flatMap(Collection::stream)
.collect(Collectors.toList());
As a bonus, since Stream.of() is variadic, you may concatenate as many lists as you like.
List<String> newList = Stream.of(listOne, listTwo, listThree)
.flatMap(Collection::stream)
.collect(Collectors.toList());
One of your requirements is to preserve the original lists. If you create a new list and use addAll(), you are effectively doubling the number of references to the objects in your lists. This could lead to memory problems if your lists are very large.
If you don't need to modify the concatenated result, you can avoid this using a custom list implementation. The custom implementation class is more than one line, obviously...but using it is short and sweet.
CompositeUnmodifiableList.java:
public class CompositeUnmodifiableList<E> extends AbstractList<E> {
private final List<? extends E> list1;
private final List<? extends E> list2;
public CompositeUnmodifiableList(List<? extends E> list1, List<? extends E> list2) {
this.list1 = list1;
this.list2 = list2;
}
#Override
public E get(int index) {
if (index < list1.size()) {
return list1.get(index);
}
return list2.get(index-list1.size());
}
#Override
public int size() {
return list1.size() + list2.size();
}
}
Usage:
List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
Probably not simpler, but intriguing and ugly:
List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };
Don't use it in production code... ;)
Not simpler, but without resizing overhead:
List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);
Found this question looking to concatenate arbitrary amount of lists, not minding external libraries. So, perhaps it will help someone else:
com.google.common.collect.Iterables#concat()
Useful if you want to apply the same logic to a number of different collections in one for().
Java 8 (Stream.of and Stream.concat)
The proposed solution is for three lists though it can be applied for two lists as well. In Java 8 we can make use of Stream.of or Stream.concat as:
List<String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList());
List<String> result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList());
Stream.concat takes two streams as input and creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream. As we have three lists we have used this method (Stream.concat) two times.
We can also write a utility class with a method that takes any number of lists (using varargs) and returns a concatenated list as:
public static <T> List<T> concatenateLists(List<T>... collections) {
return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList());
}
Then we can make use of this method as:
List<String> result3 = Utils.concatenateLists(list1,list2,list3);
Here is a java 8 solution using two lines:
List<Object> newList = new ArrayList<>();
Stream.of(list1, list2).forEach(newList::addAll);
Be aware that this method should not be used if
the origin of newList is not known and it may already be shared with other threads
the stream that modifies newList is a parallel stream and access to newList is not synchronized or threadsafe
due to side effect considerations.
Both of the above conditions do not apply for the above case of joining two lists, so this is safe.
Based on this answer to another question.
This is simple and just one line, but will add the contents of listTwo to listOne. Do you really need to put the contents in a third list?
Collections.addAll(listOne, listTwo.toArray());
Slightly simpler:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
A little shorter would be:
List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
You can create your generic Java 8 utility method to concat any number of lists.
#SafeVarargs
public static <T> List<T> concat(List<T>... lists) {
return Stream.of(lists).flatMap(List::stream).collect(Collectors.toList());
}
In Java 8 (the other way):
List<?> newList =
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
You can do a oneliner if the target list is predeclared.
(newList = new ArrayList<String>(list1)).addAll(list2);
another one liner solution using Java8 stream, since flatMap solution is already posted, here is a solution without flatMap
List<E> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
or
List<E> ints = Stream.of(list1, list2).collect(ArrayList::new, List::addAll, List::addAll);
code
List<List<Integer>> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
List<Integer> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
System.out.println(lol);
System.out.println(li);
output
[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
We can join 2 lists using java8 with 2 approaches.
List<String> list1 = Arrays.asList("S", "T");
List<String> list2 = Arrays.asList("U", "V");
1) Using concat :
List<String> collect2 = Stream.concat(list1.stream(), list2.stream()).collect(toList());
System.out.println("collect2 = " + collect2); // collect2 = [S, T, U, V]
2) Using flatMap :
List<String> collect3 = Stream.of(list1, list2).flatMap(Collection::stream).collect(toList());
System.out.println("collect3 = " + collect3); // collect3 = [S, T, U, V]
Almost of answers suggest to use an ArrayList.
List<String> newList = new LinkedList<>(listOne);
newList.addAll(listTwo);
Prefer to use a LinkedList for efficient add operations.
ArrayList add is O(1) amortized, but O(n) worst-case since the array must be resized and copied.
While LinkedList add is always constant O(1).
more infos https://stackoverflow.com/a/322742/311420
The smartest in my opinion:
/**
* #param smallLists
* #return one big list containing all elements of the small ones, in the same order.
*/
public static <E> List<E> concatenate (final List<E> ... smallLists)
{
final ArrayList<E> bigList = new ArrayList<E>();
for (final List<E> list: smallLists)
{
bigList.addAll(list);
}
return bigList;
}
You could do it with a static import and a helper class
nb the generification of this class could probably be improved
public class Lists {
private Lists() { } // can't be instantiated
public static List<T> join(List<T>... lists) {
List<T> result = new ArrayList<T>();
for(List<T> list : lists) {
result.addAll(list);
}
return results;
}
}
Then you can do things like
import static Lists.join;
List<T> result = join(list1, list2, list3, list4);
Java 8 version with support for joining by object key:
public List<SomeClass> mergeLists(final List<SomeClass> left, final List<SomeClass> right, String primaryKey) {
final Map<Object, SomeClass> mergedList = new LinkedHashMap<>();
Stream.concat(left.stream(), right.stream())
.map(someObject -> new Pair<Object, SomeClass>(someObject.getSomeKey(), someObject))
.forEach(pair-> mergedList.put(pair.getKey(), pair.getValue()));
return new ArrayList<>(mergedList.values());
}
public static <T> List<T> merge(List<T>... args) {
final List<T> result = new ArrayList<>();
for (List<T> list : args) {
result.addAll(list);
}
return result;
}
Use a Helper class.
I suggest:
public static <E> Collection<E> addAll(Collection<E> dest, Collection<? extends E>... src) {
for(Collection<? extends E> c : src) {
dest.addAll(c);
}
return dest;
}
public static void main(String[] args) {
System.out.println(addAll(new ArrayList<Object>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));
// does not compile
// System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));
System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6)));
}
public static <T> List<T> merge(#Nonnull final List<T>... list) {
// calculate length first
int mergedLength = 0;
for (List<T> ts : list) {
mergedLength += ts.size();
}
final List<T> mergedList = new ArrayList<>(mergedLength);
for (List<T> ts : list) {
mergedList.addAll(ts);
}
return mergedList;
}
My favourite way, using fluent api and Guava:
List<String> combined = ImmutableList.<String>builder().addAll(list1).addAll(list2).build()
I'm not claiming that it's simple, but you mentioned bonus for one-liners ;-)
Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
new Vector(list1).elements(),
new Vector(list2).elements(),
...
}))
No way near one-liner, but I think this is the simplest:
List<String> newList = new ArrayList<String>(l1);
newList.addAll(l2);
for(String w:newList)
System.out.printf("%s ", w);
Here's an approach using streams and java 8 if your lists have different types and you want to combine them to a list of another type.
public static void main(String[] args) {
List<String> list2 = new ArrayList<>();
List<Pair<Integer, String>> list1 = new ArrayList<>();
list2.add("asd");
list2.add("asdaf");
list1.add(new Pair<>(1, "werwe"));
list1.add(new Pair<>(2, "tyutyu"));
Stream stream = Stream.concat(list1.stream(), list2.stream());
List<Pair<Integer, String>> res = (List<Pair<Integer, String>>) stream
.map(item -> {
if (item instanceof String) {
return new Pair<>(0, item);
}
else {
return new Pair<>(((Pair<Integer, String>)item).getKey(), ((Pair<Integer, String>)item).getValue());
}
})
.collect(Collectors.toList());
}
If you want to do this statically you can the following.
The examples uses 2 EnumSets in natural-order (==Enum-order) A, B and joins then in an ALL list.
public static final EnumSet<MyType> CATEGORY_A = EnumSet.of(A_1, A_2);
public static final EnumSet<MyType> CATEGORY_B = EnumSet.of(B_1, B_2, B_3);
public static final List<MyType> ALL =
Collections.unmodifiableList(
new ArrayList<MyType>(CATEGORY_A.size() + CATEGORY_B.size())
{{
addAll(CATEGORY_A);
addAll(CATEGORY_B);
}}
);
I'm trying to split a list into a list of list where each list has a maximum size of 4.
I would like to know how this is possible to do using lambdas.
Currently the way I'm doing it is as follow:
List<List<Object>> listOfList = new ArrayList<>();
final int MAX_ROW_LENGTH = 4;
int startIndex =0;
while(startIndex <= listToSplit.size() )
{
int endIndex = ( ( startIndex+MAX_ROW_LENGTH ) < listToSplit.size() ) ? startIndex+MAX_ROW_LENGTH : listToSplit.size();
listOfList.add(new ArrayList<>(listToSplit.subList(startIndex, endIndex)));
startIndex = startIndex+MAX_ROW_LENGTH;
}
UPDATE
It seems that there isn't a simple way to use lambdas to split lists. While all of the answers are much appreciated, they're also a wonderful example of when lambdas do not simplify things.
Try this approach:
static <T> List<List<T>> listSplitter(List<T> incoming, int size) {
// add validation if needed
return incoming.stream()
.collect(Collector.of(
ArrayList::new,
(accumulator, item) -> {
if(accumulator.isEmpty()) {
accumulator.add(new ArrayList<>(singletonList(item)));
} else {
List<T> last = accumulator.get(accumulator.size() - 1);
if(last.size() == size) {
accumulator.add(new ArrayList<>(singletonList(item)));
} else {
last.add(item);
}
}
},
(li1, li2) -> {
li1.addAll(li2);
return li1;
}
));
}
System.out.println(
listSplitter(
Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
4
)
);
Also note that this code could be optimized, instead of:
new ArrayList<>(Collections.singletonList(item))
use this one:
List<List<T>> newList = new ArrayList<>(size);
newList.add(item);
return newList;
If you REALLY need a lambda it can be done like this. Otherwise the previous answers are better.
List<List<Object>> lists = new ArrayList<>();
AtomicInteger counter = new AtomicInteger();
final int MAX_ROW_LENGTH = 4;
listToSplit.forEach(pO -> {
if(counter.getAndIncrement() % MAX_ROW_LENGTH == 0) {
lists.add(new ArrayList<>());
}
lists.get(lists.size()-1).add(pO);
});
Surely the below is sufficient
final List<List<Object>> listOfList = new ArrayList<>(
listToSplit.stream()
.collect(Collectors.groupingBy(el -> listToSplit.indexOf(el) / MAX_ROW_LENGTH))
.values()
);
Stream it, collect with a grouping: this gives a Map of Object -> List, pull the values of the map and pass directly into whatever constructor (map.values() gives a Collection not a List).
Perhaps you can use something like that
BiFunction<List,Integer,List> splitter= (list2, count)->{
//temporary list of lists
List<List> listOfLists=new ArrayList<>();
//helper implicit recursive function
BiConsumer<Integer,BiConsumer> splitterHelper = (offset, func) -> {
if(list2.size()> offset+count){
listOfLists.add(list2.subList(offset,offset+count));
//implicit self call
func.accept(offset+count,func);
}
else if(list2.size()>offset){
listOfLists.add(list2.subList(offset,list2.size()));
//implicit self call
func.accept(offset+count,func);
}
};
//pass self reference
splitterHelper.accept(0,splitterHelper);
return listOfLists;
};
Usage example
List<Integer> list=new ArrayList<Integer>(){{
add(1);
add(2);
add(3);
add(4);
add(5);
add(6);
add(7);
add(8);
add(8);
}};
//calling splitter function
List listOfLists = splitter.apply(list, 3 /*max sublist size*/);
System.out.println(listOfLists);
And as a result we have
[[1, 2, 3], [4, 5, 6], [7, 8, 8]]
The requirement is a bit odd, but you could do:
final int[] counter = new int[] {0};
List<List<Object>> listOfLists = in.stream()
.collect(Collectors.groupingBy( x -> counter[0]++ / MAX_ROW_LENGTH ))
.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
You could probably streamline this by using the variant of groupingBy that takes a mapSupplier lambda, and supplying a SortedMap. This should return an EntrySet that iterates in order. I leave it as an exercise.
What we're doing here is:
Collecting your list items into a Map<Integer,Object> using a counter to group. The counter is held in a single-element array because the lambda can only use local variables if they're final.
Getting the map entries as a stream, and sorting by the Integer key.
Using Stream::map() to convert the stream of Map.Entry<Integer,Object> into a stream of Object values.
Collecting this into a list.
This doesn't benefit from any "free" parallelisation. It has a memory overhead in the intermediate Map. It's not particularly easy to read.
However, I wouldn't do this, just for the sake of using a lambda. I would do something like:
for(int i=0; i<in.size(); i += MAX_ROW_LENGTH) {
listOfList.add(
listToSplit.subList(i, Math.min(i + MAX_ROW_LENGTH, in.size());
}
(Yours had a defensive copy new ArrayList<>(listToSplit.subList(...)). I've not duplicated it because it's not always necessary - for example if the input list is unmodifiable and the output lists aren't intended to be modifiable. But do put it back in if you decide you need it in your case.)
This will be extremely fast on any in-memory list. You're very unlikely to want to parallelise it.
Alternatively, you could write your own (unmodifiable) implementation of List that's a view over the underlying List<Object>:
public class PartitionedList<T> extends AbstractList<List<T>> {
private final List<T> source;
private final int sublistSize;
public PartitionedList(T source, int sublistSize) {
this.source = source;
this.sublistSize = sublistSize;
}
#Override
public int size() {
return source.size() / sublistSize;
}
#Override
public List<T> get(int index) {
int sourceIndex = index * sublistSize
return source.subList(sourceIndex,
Math.min(sourceIndex + sublistSize, source.size());
}
}
Again, it's up to you whether you want to make defensive copies here.
This will be have equivalent big-O access time to the underlying list.
You can use:
ListUtils.partition(List list, int size)
OR
List<List> partition(List list, int size)
Both return consecutive sublists of a list, each of the same size (the final list may be smaller).
I'm having two collections of Foo. I can not change the implementation of Foo and Foo's equals function is implemented incorrectly. I can also not inherit from Foo. I do custom equality function: I've implemented using guava's Predicate function. To give you an idea, the implementation looks a bit like this.
new Predicate<Pair<Foo, Foo>>() {
#Override
public boolean apply(#Nullable Pair<Foo, Foo> input) {
Foo one = input.getFirst();
Foo two = input.getSecond();
return Objects.equals(one.getId(), two.getId());
}
};
Now I need to check if my two collections of Foo contain the same items ignoring the order
I'm looking for the best way to do this using this custom equality function.
You could wrap your class in a Guava Equivalence and store that in the Sets.
Equivalence<Foo> eq = new Equivalence<Foo>{
// implement equals and hashcode
};
Set<Equivalence<Foo>> set1 = new HashSet<>();
set1.add(eq.wrap(someFoo));
That way you could do a bidirectional containsAll() or do
Sets.difference(set1, set2).isEmpty()
Instead of a custom Predicate, why not a simple SortedSet with custom Comparator?
Comparator<Foo> comparator = new Comparator<Foo>() {
public int compare(Foo o1, Foo o2) {
return //your custom comparison
}
};
SortedSet<Foo> sortedSet1 = newTreeSet(comparator);
sortedSet1.addAll(firstCollection);
SortedSet<Foo> sortedSet2 = newTreeSet(comparator);
sortedSet2.addAll(secondCollection);
sortedSet1.equals(sortedSet); //this is what you want
If you don't want to have sorted list after operation, copy it or use answer with Set (but with Set [1,1,1] == [1]).
public class ListTest {
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> list3 = Arrays.asList(1, 2, 3, 4, 4);
System.out.println(compare(list1, list2, (a, b) -> a - b));
System.out.println(compare(list1, list3, (a, b) -> a - b));
}
private static <E> boolean compare(List<E> list1, List<E> list2, Comparator<E> comparator) {
if(list1.size() != list2.size()) {
return false;
}
Collections.sort(list1, comparator);
Collections.sort(list2, comparator);
Iterator<E> iterator1 = list1.iterator();
Iterator<E> iterator2 = list2.iterator();
while (iterator1.hasNext()) {
if(comparator.compare(iterator1.next(), iterator2.next()) != 0) {
return false;
}
}
return true;
}
}
I am working in Java. I have two lists, let's call them A and B, which I want to sort.
A is an Integer list, so I have no problem to do that. I simply use Collections.sort() to obtain a sorted list of integers.
The problem comes with the list B. I want to make the same changes done before in A.. B is a list of objects, but there's no way to associate the changes in B with changes in A. I mean, there's no condition to create a comparator method.
Little example:
I have:
A -> {5,1,3,6,4}
B -> {a,b,c,d,e}
I want to sort A and apply the same changes to B to obtain:
A -> {1,3,4,5,6}
B -> {b,c,e,a,d}
Is there any way to do that using built-in Java functions? I prefer to avoid writing a sorting algorithm myself because of efficiency.
Thank you!
A TreeMap will always iterate over the keys in the right order, so you can do it like this:
Map<Integer, String> map = new TreeMap<Integer, String>();
map.put(5, "a");
map.put(1, "b");
map.put(3, "c");
map.put(6, "d");
map.put(4, "e");
System.out.println(map.keySet());
System.out.println(map.values());
However if you really want to start and end with the same pair of List instances, I think you'd have to do something convoluted like this:
List<Integer> numbers = new ArrayList<Integer>(Arrays.asList(5, 1, 3, 6, 4));
List<String> letters = new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e"));
Map<Integer, String> map = new HashMap<Integer, String>();
for (int i = 0, n = numbers.size(); i < n; i++) {
map.put(numbers.get(i), letters.get(i));
}
Collections.sort(numbers);
letters.clear();
for (int number : numbers) {
letters.add(map.get(number));
}
System.out.println(numbers);
System.out.println(letters);
I would start by creating a POJO to store A and B,
static class ABPojo implements Comparable<ABPojo> {
public ABPojo(int a, String b) {
this.a = a;
this.b = b;
}
private int a;
private String b;
public int getA() {
return a;
}
public String getB() {
return b;
}
public int compareTo(ABPojo o) {
if (o instanceof ABPojo) {
ABPojo that = (ABPojo) o;
return Integer.valueOf(a).compareTo(that.getA());
}
return 1;
}
}
Then you can loop over the collection of ABPojo(s) after sorting to build your output with something like
public static void main(String[] args) {
List<ABPojo> al = new ArrayList<ABPojo>();
al.add(new ABPojo(5, "a"));
al.add(new ABPojo(1, "b"));
al.add(new ABPojo(3, "c"));
al.add(new ABPojo(6, "d"));
al.add(new ABPojo(4, "e"));
Collections.sort(al);
StringBuilder a = new StringBuilder();
StringBuilder b = new StringBuilder();
for (ABPojo pojo : al) {
if (a.length() > 0) {
a.append(",");
} else {
a.append("{");
}
if (b.length() > 0) {
b.append(",");
} else {
b.append("{");
}
a.append(pojo.getA());
b.append(pojo.getB());
}
a.append("}");
b.append("}");
System.out.println("A -> " + a.toString());
System.out.println("B -> " + b.toString());
}
Output is the requested
A -> {1,3,4,5,6}
B -> {b,c,e,a,d}
Create a map with your elements in A as key and the elements in B as value resp.
Then Collections.Sort() will automatically sort the A elements and its corresponding B elements.
Start with here:
How to find the permutation of a sort in Java
Then manually apply the permutation.
But it sounds like a design problem; consider one list of classes that implement Comparator<T> where the comparison function is just on the numbers.
I don't think there is a Java function that will do this.
However, you could use a map structure instead of a list stucture where the key to the data is your int, and the data is the unsortable list.
General question: What's the proper way to reverse a stream? Assuming that we don't know what type of elements that stream consists of, what's the generic way to reverse any stream?
Specific question:
IntStream provides range method to generate Integers in specific range IntStream.range(-range, 0), now that I want to reverse it switching range from 0 to negative won't work, also I can't use Integer::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
with IntStream I'll get this compiler error
Error:(191, 0) ajc: The method sorted() in the type IntStream is not applicable for the arguments (Integer::compare)
what am I missing here?
For the specific question of generating a reverse IntStream, try something like this:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to)
.map(i -> to - i + from - 1);
}
This avoids boxing and sorting.
For the general question of how to reverse a stream of any type, I don't know of there's a "proper" way. There are a couple ways I can think of. Both end up storing the stream elements. I don't know of a way to reverse a stream without storing the elements.
This first way stores the elements into an array and reads them out to a stream in reverse order. Note that since we don't know the runtime type of the stream elements, we can't type the array properly, requiring an unchecked cast.
#SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
Object[] temp = input.toArray();
return (Stream<T>) IntStream.range(0, temp.length)
.mapToObj(i -> temp[temp.length - i - 1]);
}
Another technique uses collectors to accumulate the items into a reversed list. This does lots of insertions at the front of ArrayList objects, so there's lots of copying going on.
Stream<T> input = ... ;
List<T> output =
input.collect(ArrayList::new,
(list, e) -> list.add(0, e),
(list1, list2) -> list1.addAll(0, list2));
It's probably possible to write a much more efficient reversing collector using some kind of customized data structure.
UPDATE 2016-01-29
Since this question has gotten a bit of attention recently, I figure I should update my answer to solve the problem with inserting at the front of ArrayList. This will be horribly inefficient with a large number of elements, requiring O(N^2) copying.
It's preferable to use an ArrayDeque instead, which efficiently supports insertion at the front. A small wrinkle is that we can't use the three-arg form of Stream.collect(); it requires the contents of the second arg be merged into the first arg, and there's no "add-all-at-front" bulk operation on Deque. Instead, we use addAll() to append the contents of the first arg to the end of the second, and then we return the second. This requires using the Collector.of() factory method.
The complete code is this:
Deque<String> output =
input.collect(Collector.of(
ArrayDeque::new,
(deq, t) -> deq.addFirst(t),
(d1, d2) -> { d2.addAll(d1); return d2; }));
The result is a Deque instead of a List, but that shouldn't be much of an issue, as it can easily be iterated or streamed in the now-reversed order.
Elegant solution
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream()
.sorted(Collections.reverseOrder()) // Method on Stream<Integer>
.forEach(System.out::println);
General Question:
Stream does not store any elements.
So iterating elements in the reverse order is not possible without storing the elements in some intermediate collection.
Stream.of("1", "2", "20", "3")
.collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
.descendingIterator()
.forEachRemaining(System.out::println);
Update: Changed LinkedList to ArrayDeque (better) see here for details
Prints:
3
20
2
1
By the way, using sort method is not correct as it sorts, NOT reverses (assuming stream may have unordered elements)
Specific Question:
I found this simple, easier and intuitive(Copied #Holger comment)
IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
Many of the solutions here sort or reverse the IntStream, but that unnecessarily requires intermediate storage. Stuart Marks's solution is the way to go:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to).map(i -> to - i + from - 1);
}
It correctly handles overflow as well, passing this test:
#Test
public void testRevRange() {
assertArrayEquals(revRange(0, 5).toArray(), new int[]{4, 3, 2, 1, 0});
assertArrayEquals(revRange(-5, 0).toArray(), new int[]{-1, -2, -3, -4, -5});
assertArrayEquals(revRange(1, 4).toArray(), new int[]{3, 2, 1});
assertArrayEquals(revRange(0, 0).toArray(), new int[0]);
assertArrayEquals(revRange(0, -1).toArray(), new int[0]);
assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE).toArray(), new int[0]);
assertArrayEquals(revRange(MAX_VALUE, MAX_VALUE).toArray(), new int[0]);
assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE + 1).toArray(), new int[]{MIN_VALUE});
assertArrayEquals(revRange(MAX_VALUE - 1, MAX_VALUE).toArray(), new int[]{MAX_VALUE - 1});
}
How NOT to do it:
Don't use .sorted(Comparator.reverseOrder()) or .sorted(Collections.reverseOrder()), because it will just sort elements in the descending order.
Using it for given Integer input:
[1, 4, 2, 5, 3]
the output would be as follows:
[5, 4, 3, 2, 1]
For String input:
["A", "D", "B", "E", "C"]
the output would be as follows:
[E, D, C, B, A]
Don't use .sorted((a, b) -> -1) (explanation at the end)
The easiest way to do it properly:
List<Integer> list = Arrays.asList(1, 4, 2, 5, 3);
Collections.reverse(list);
System.out.println(list);
Output:
[3, 5, 2, 4, 1]
The same for String:
List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
Collections.reverse(stringList);
System.out.println(stringList);
Output:
[C, E, B, D, A]
Don't use .sorted((a, b) -> -1)!
It breaks comparator contract and might work only for some cases ie. only on single thread but not in parallel.
yankee explanation:
(a, b) -> -1 breaks the contract for Comparator. Whether this works depends on the implementation of the sort algorithm. The next release of the JVM might break this. Actually I can already break this reproduciblly on my machine using IntStream.range(0, 10000).parallel().boxed().sorted((a, b) -> -1).forEachOrdered(System.out::println);
//Don't use this!!!
List<Integer> list = Arrays.asList(1, 4, 2, 5, 3);
List<Integer> reversedList = list.stream()
.sorted((a, b) -> -1)
.collect(Collectors.toList());
System.out.println(reversedList);
Output in positive case:
[3, 5, 2, 4, 1]
Possible output in parallel stream or with other JVM implementation:
[4, 1, 2, 3, 5]
The same for String:
//Don't use this!!!
List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
List<String> reversedStringList = stringList.stream()
.sorted((a, b) -> -1)
.collect(Collectors.toList());
System.out.println(reversedStringList);
Output in positive case:
[C, E, B, D, A]
Possible output in parallel stream or with other JVM implementation:
[A, E, B, D, C]
without external lib...
import java.util.List;
import java.util.Collections;
import java.util.stream.Collector;
public class MyCollectors {
public static <T> Collector<T, ?, List<T>> toListReversed() {
return Collectors.collectingAndThen(Collectors.toList(), l -> {
Collections.reverse(l);
return l;
});
}
}
If implemented Comparable<T> (ex. Integer, String, Date), you can do it using Comparator.reverseOrder().
List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);
You could define your own collector that collects the elements in reverse order:
public static <T> Collector<T, List<T>, List<T>> inReverse() {
return Collector.of(
ArrayList::new,
(l, t) -> l.add(t),
(l, r) -> {l.addAll(r); return l;},
Lists::<T>reverse);
}
And use it like:
stream.collect(inReverse()).forEach(t -> ...)
I use an ArrayList in forward order to efficiently insert collect the items (at the end of the list), and Guava Lists.reverse to efficiently give a reversed view of the list without making another copy of it.
Here are some test cases for the custom collector:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import org.hamcrest.Matchers;
import org.junit.Test;
import com.google.common.collect.Lists;
public class TestReverseCollector {
private final Object t1 = new Object();
private final Object t2 = new Object();
private final Object t3 = new Object();
private final Object t4 = new Object();
private final Collector<Object, List<Object>, List<Object>> inReverse = inReverse();
private final Supplier<List<Object>> supplier = inReverse.supplier();
private final BiConsumer<List<Object>, Object> accumulator = inReverse.accumulator();
private final Function<List<Object>, List<Object>> finisher = inReverse.finisher();
private final BinaryOperator<List<Object>> combiner = inReverse.combiner();
#Test public void associative() {
final List<Object> a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
final List<Object> r1 = finisher.apply(a1);
final List<Object> a2 = supplier.get();
accumulator.accept(a2, t1);
final List<Object> a3 = supplier.get();
accumulator.accept(a3, t2);
final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));
assertThat(r1, Matchers.equalTo(r2));
}
#Test public void identity() {
final List<Object> a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
final List<Object> r1 = finisher.apply(a1);
final List<Object> a2 = supplier.get();
accumulator.accept(a2, t1);
accumulator.accept(a2, t2);
final List<Object> r2 = finisher.apply(combiner.apply(a2, supplier.get()));
assertThat(r1, equalTo(r2));
}
#Test public void reversing() throws Exception {
final List<Object> a2 = supplier.get();
accumulator.accept(a2, t1);
accumulator.accept(a2, t2);
final List<Object> a3 = supplier.get();
accumulator.accept(a3, t3);
accumulator.accept(a3, t4);
final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));
assertThat(r2, contains(t4, t3, t2, t1));
}
public static <T> Collector<T, List<T>, List<T>> inReverse() {
return Collector.of(
ArrayList::new,
(l, t) -> l.add(t),
(l, r) -> {l.addAll(r); return l;},
Lists::<T>reverse);
}
}
cyclops-react StreamUtils has a reverse Stream method (javadoc).
StreamUtils.reverse(Stream.of("1", "2", "20", "3"))
.forEach(System.out::println);
It works by collecting to an ArrayList and then making use of the ListIterator class which can iterate in either direction, to iterate backwards over the list.
If you already have a List, it will be more efficient
StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
.forEach(System.out::println);
Here's the solution I've come up with:
private static final Comparator<Integer> BY_ASCENDING_ORDER = Integer::compare;
private static final Comparator<Integer> BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();
then using those comparators:
IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
I would suggest using jOOλ, it's a great library that adds lots of useful functionality to Java 8 streams and lambdas.
You can then do the following:
List<Integer> list = Arrays.asList(1,2,3,4);
Seq.seq(list).reverse().forEach(System.out::println)
Simple as that. It's a pretty lightweight library, and well worth adding to any Java 8 project.
How about this utility method?
public static <T> Stream<T> getReverseStream(List<T> list) {
final ListIterator<T> listIt = list.listIterator(list.size());
final Iterator<T> reverseIterator = new Iterator<T>() {
#Override
public boolean hasNext() {
return listIt.hasPrevious();
}
#Override
public T next() {
return listIt.previous();
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
reverseIterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
Seems to work with all cases without duplication.
With regard to the specific question of generating a reverse IntStream:
starting from Java 9 you can use the three-argument version of the IntStream.iterate(...):
IntStream.iterate(10, x -> x >= 0, x -> x - 1).forEach(System.out::println);
// Out: 10 9 8 7 6 5 4 3 2 1 0
where:
IntStream.iterate(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed - the initial element;
hasNext - a predicate to apply to elements to determine when the
stream must terminate;
next - a function to be applied to the previous element to produce a
new element.
Simplest way (simple collect - supports parallel streams):
public static <T> Stream<T> reverse(Stream<T> stream) {
return stream
.collect(Collector.of(
() -> new ArrayDeque<T>(),
ArrayDeque::addFirst,
(q1, q2) -> { q2.addAll(q1); return q2; })
)
.stream();
}
Advanced way (supports parallel streams in an ongoing way):
public static <T> Stream<T> reverse(Stream<T> stream) {
Objects.requireNonNull(stream, "stream");
class ReverseSpliterator implements Spliterator<T> {
private Spliterator<T> spliterator;
private final Deque<T> deque = new ArrayDeque<>();
private ReverseSpliterator(Spliterator<T> spliterator) {
this.spliterator = spliterator;
}
#Override
#SuppressWarnings({"StatementWithEmptyBody"})
public boolean tryAdvance(Consumer<? super T> action) {
while(spliterator.tryAdvance(deque::addFirst));
if(!deque.isEmpty()) {
action.accept(deque.remove());
return true;
}
return false;
}
#Override
public Spliterator<T> trySplit() {
// After traveling started the spliterator don't contain elements!
Spliterator<T> prev = spliterator.trySplit();
if(prev == null) {
return null;
}
Spliterator<T> me = spliterator;
spliterator = prev;
return new ReverseSpliterator(me);
}
#Override
public long estimateSize() {
return spliterator.estimateSize();
}
#Override
public int characteristics() {
return spliterator.characteristics();
}
#Override
public Comparator<? super T> getComparator() {
Comparator<? super T> comparator = spliterator.getComparator();
return (comparator != null) ? comparator.reversed() : null;
}
#Override
public void forEachRemaining(Consumer<? super T> action) {
// Ensure that tryAdvance is called at least once
if(!deque.isEmpty() || tryAdvance(action)) {
deque.forEach(action);
}
}
}
return StreamSupport.stream(new ReverseSpliterator(stream.spliterator()), stream.isParallel());
}
Note you can quickly extends to other type of streams (IntStream, ...).
Testing:
// Use parallel if you wish only
revert(Stream.of("One", "Two", "Three", "Four", "Five", "Six").parallel())
.forEachOrdered(System.out::println);
Results:
Six
Five
Four
Three
Two
One
Additional notes: The simplest way it isn't so useful when used with other stream operations (the collect join breaks the parallelism). The advance way doesn't have that issue, and it keeps also the initial characteristics of the stream, for example SORTED, and so, it's the way to go to use with other stream operations after the reverse.
ArrayDeque are faster in the stack than a Stack or LinkedList. "push()" inserts elements at the front of the Deque
protected <T> Stream<T> reverse(Stream<T> stream) {
ArrayDeque<T> stack = new ArrayDeque<>();
stream.forEach(stack::push);
return stack.stream();
}
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
newStream.forEach(System.out::println);
One could write a collector that collects elements in reversed order:
public static <T> Collector<T, ?, Stream<T>> reversed() {
return Collectors.collectingAndThen(Collectors.toList(), list -> {
Collections.reverse(list);
return list.stream();
});
}
And use it like this:
Stream.of(1, 2, 3, 4, 5).collect(reversed()).forEach(System.out::println);
Original answer (contains a bug - it does not work correctly for parallel streams):
A general purpose stream reverse method could look like:
public static <T> Stream<T> reverse(Stream<T> stream) {
LinkedList<T> stack = new LinkedList<>();
stream.forEach(stack::push);
return stack.stream();
}
For reference I was looking at the same problem, I wanted to join the string value of stream elements in the reverse order.
itemList = { last, middle, first } => first,middle,last
I started to use an intermediate collection with collectingAndThen from comonad or the ArrayDeque collector of Stuart Marks, although I wasn't happy with intermediate collection, and streaming again
itemList.stream()
.map(TheObject::toString)
.collect(Collectors.collectingAndThen(Collectors.toList(),
strings -> {
Collections.reverse(strings);
return strings;
}))
.stream()
.collect(Collector.joining());
So I iterated over Stuart Marks answer that was using the Collector.of factory, that has the interesting finisher lambda.
itemList.stream()
.collect(Collector.of(StringBuilder::new,
(sb, o) -> sb.insert(0, o),
(r1, r2) -> { r1.insert(0, r2); return r1; },
StringBuilder::toString));
Since in this case the stream is not parallel, the combiner is not relevant that much, I'm using insert anyway for the sake of code consistency but it does not matter as it would depend of which stringbuilder is built first.
I looked at the StringJoiner, however it does not have an insert method.
Not purely Java8 but if you use guava's Lists.reverse() method in conjunction, you can easily achieve this:
List<Integer> list = Arrays.asList(1,2,3,4);
Lists.reverse(list).stream().forEach(System.out::println);
Reversing string or any Array
(Stream.of("abcdefghijklm 1234567".split("")).collect(Collectors.collectingAndThen(Collectors.toList(),list -> {Collections.reverse(list);return list;}))).stream().forEach(System.out::println);
split can be modified based on the delimiter or space
How about reversing the Collection backing the stream prior?
import java.util.Collections;
import java.util.List;
public void reverseTest(List<Integer> sampleCollection) {
Collections.reverse(sampleCollection); // remember this reverses the elements in the list, so if you want the original input collection to remain untouched clone it first.
sampleCollection.stream().forEach(item -> {
// you op here
});
}
Answering specific question of reversing with IntStream, below worked for me:
IntStream.range(0, 10)
.map(x -> x * -1)
.sorted()
.map(Math::abs)
.forEach(System.out::println);
In all this I don't see the answer I would go to first.
This isn't exactly a direct answer to the question, but it's a potential solution to the problem.
Just build the list backwards in the first place. If you can, use a LinkedList instead of an ArrayList and when you add items use "Push" instead of add. The list will be built in the reverse order and will then stream correctly without any manipulation.
This won't fit cases where you are dealing with primitive arrays or lists that are already used in various ways but does work well in a surprising number of cases.
the simplest solution is using List::listIterator and Stream::generate
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
ListIterator<Integer> listIterator = list.listIterator(list.size());
Stream.generate(listIterator::previous)
.limit(list.size())
.forEach(System.out::println);
This method works with any Stream and is Java 8 compliant:
Stream<Integer> myStream = Stream.of(1, 2, 3, 4, 5);
myStream.reduce(Stream.empty(),
(Stream<Integer> a, Integer b) -> Stream.concat(Stream.of(b), a),
(a, b) -> Stream.concat(b, a))
.forEach(System.out::println);
This is how I do it.
I don't like the idea of creating a new collection and reverse iterating it.
The IntStream#map idea is pretty neat, but I prefer the IntStream#iterate method, for I think the idea of a countdown to Zero better expressed with the iterate method and easier to understand in terms of walking the array from back to front.
import static java.lang.Math.max;
private static final double EXACT_MATCH = 0d;
public static IntStream reverseStream(final int[] array) {
return countdownFrom(array.length - 1).map(index -> array[index]);
}
public static DoubleStream reverseStream(final double[] array) {
return countdownFrom(array.length - 1).mapToDouble(index -> array[index]);
}
public static <T> Stream<T> reverseStream(final T[] array) {
return countdownFrom(array.length - 1).mapToObj(index -> array[index]);
}
public static IntStream countdownFrom(final int top) {
return IntStream.iterate(top, t -> t - 1).limit(max(0, (long) top + 1));
}
Here are some tests to prove it works:
import static java.lang.Integer.MAX_VALUE;
import static org.junit.Assert.*;
#Test
public void testReverseStream_emptyArrayCreatesEmptyStream() {
Assert.assertEquals(0, reverseStream(new double[0]).count());
}
#Test
public void testReverseStream_singleElementCreatesSingleElementStream() {
Assert.assertEquals(1, reverseStream(new double[1]).count());
final double[] singleElementArray = new double[] { 123.4 };
assertArrayEquals(singleElementArray, reverseStream(singleElementArray).toArray(), EXACT_MATCH);
}
#Test
public void testReverseStream_multipleElementsAreStreamedInReversedOrder() {
final double[] arr = new double[] { 1d, 2d, 3d };
final double[] revArr = new double[] { 3d, 2d, 1d };
Assert.assertEquals(arr.length, reverseStream(arr).count());
Assert.assertArrayEquals(revArr, reverseStream(arr).toArray(), EXACT_MATCH);
}
#Test
public void testCountdownFrom_returnsAllElementsFromTopToZeroInReverseOrder() {
assertArrayEquals(new int[] { 4, 3, 2, 1, 0 }, countdownFrom(4).toArray());
}
#Test
public void testCountdownFrom_countingDownStartingWithZeroOutputsTheNumberZero() {
assertArrayEquals(new int[] { 0 }, countdownFrom(0).toArray());
}
#Test
public void testCountdownFrom_doesNotChokeOnIntegerMaxValue() {
assertEquals(true, countdownFrom(MAX_VALUE).anyMatch(x -> x == MAX_VALUE));
}
#Test
public void testCountdownFrom_givesZeroLengthCountForNegativeValues() {
assertArrayEquals(new int[0], countdownFrom(-1).toArray());
assertArrayEquals(new int[0], countdownFrom(-4).toArray());
}
Based on #stuart-marks's answer, but without casting, function returning stream of list elements starting from end:
public static <T> Stream<T> reversedStream(List<T> tList) {
final int size = tList.size();
return IntStream.range(0, size)
.mapToObj(i -> tList.get(size - 1 - i));
}
// usage
reversedStream(list).forEach(System.out::println);
What's the proper generic way to reverse a stream?
If the stream does not specify an encounter order, don't.
(!s.spliterator().hasCharacteristics(java.util.Spliterator.ORDERED))
The most generic and the easiest way to reverse a list will be :
public static <T> void reverseHelper(List<T> li){
li.stream()
.sorted((x,y)-> -1)
.collect(Collectors.toList())
.forEach(System.out::println);
}
Java 8 way to do this:
List<Integer> list = Arrays.asList(1,2,3,4);
Comparator<Integer> comparator = Integer::compare;
list.stream().sorted(comparator.reversed()).forEach(System.out::println);