static void assertStreamEquals(Stream<?> s1, Stream<?> s2) {
Iterator<?> iter1 = s1.iterator(), iter2 = s2.iterator();
while(iter1.hasNext() && iter2.hasNext())
assertEquals(iter1.next(), iter2.next());
assert !iter1.hasNext() && !iter2.hasNext();
}
Answer from ZhongYu on Stack Overflowstatic void assertStreamEquals(Stream<?> s1, Stream<?> s2) {
Iterator<?> iter1 = s1.iterator(), iter2 = s2.iterator();
while(iter1.hasNext() && iter2.hasNext())
assertEquals(iter1.next(), iter2.next());
assert !iter1.hasNext() && !iter2.hasNext();
}
Collecting the stream under test (as you show) is a straightforward and effective way of performing the test. You may create the list of expected results in the easiest way available, which might not be collecting a stream.
Alternatively, with most libraries for creating mock collaborators, one could mock a Consumer that "expects" a series of accept() calls with particular elements. Consume the Stream with it, and then "verify" that its configured expectations were met.
Videos
There cannot be a simpler solution:
- To know the duplicated ids, you must iterate over the entire collection.
- To print all the persons with duplicated ids, you must keep their full list.
As such, you will need to load the entire collection of persons in memory. There's no way around that. If you needed only the duplicate ids but not the Person objects, then you could keep just the ids with their counts, and throw away the Person objects as you go after use, that would be more efficient. (But that's not the case here.)
In any case, your solution can be more concise if you skip the intermediary map variable with the mapping of ids to lists of users:
people.stream()
.collect(Collectors.groupingBy(Person::getId)).values().stream()
.filter(peopleWithSameId -> peopleWithSameId.size() > 1)
.forEach(peopleWithSameId -> System.out.println("People with identical IDs: " + peopleWithSameId));
Btw, in case you're wondering if the .stream() there could be .parallelStream(), it would be pointless, due to the synchronization in the println method of System.out (a PrintStream). (And without synchronization println wouldn't be thread safe anyway.)
Your code and Java 8 usage looks fine in general to me.
I do see an issue with the Person class, it looks like you are intending it to be an immutable class, if so, then you should also enforce it.
You need to ensure that the name and id fields can never be changed, you can do this by adding final to them. Your code currently seems to be safe, but it is not. I can extend Person and offer a method there to change the name and id fields, which violates the assumed variant of that those fields in Person are immutable.
Simply changing it to the following will do:
public class Person {
private final String name;
private final String id;
...
}
Onto the Java 8 usage now.
It is a good thing that you use the Collectors.groupingBy to provide a Map<String, List<Person>>, you cannot do it much faster either way if you want it to work with any kind of List<Person> as input and in this way you'll save yourself from nasty bugs and reimplementing what lots of people have already done, namely the grouping by operation.
Printing the offending values using Stream seems fine as well, except that you may rewrite it to look a little bit cleaner, something like this could work:
peopleById.values().stream()
.filter(personList -> personList.size() > 1)
.forEach(personList -> System.out.println("People with identical IDs: " + personList);
This is my personal preference on how to format it though, the only real change is to rename peopleWithSameId to personList, as it is simply a List<Person> and nothing more or less.
You've done a good job overall.
My free StreamEx library allows you to process the pairs of the stream elements using additional pairMap intermediate operation. Like this:
StreamEx.of(input).pairMap((current, next) -> doSomethingWith(current, next));
Where input is a Collection, array or Stream. For example, this way you can easily check whether input is sorted:
boolean isSorted = StreamEx.of(input)
.pairMap((current, next) -> next.compareTo(current))
.allMatch(cmp -> cmp >= 0);
There's also forPairs terminal operation which is a forEach analog to all pairs of input elements:
StreamEx.of(input).forPairs((current, next) -> doSomethingWith(current, next));
These features work nicely with any stream source (either random access or not) and fully support the parallel streams.
One way is to generate an IntStream of the indices, and fetch the List elements by their index. This is only efficient if the List supports random access (i.e. if your List is a LinkedList, it would be a bad idea, since list.get(i) doesn't take constant time).
For example :
IntStream.range(0,list.size()-1).forEach(i -> {
doSomething(list.get(i),list.get(i+1));
});
Another way is to store the last element in an array :
List<Element> list = ...
Element[] arr = new Element[1];
list.stream().forEach(e -> {
if (arr[0] != null)
doSomething(arr[0],e);
arr[0]=e;
});
This will only work for sequential streams.
Let listOne.size() is N and listTwo.size() is M.
Then 2-for-loops solution has complexity of O(M*N).
We can reduce it to O(M+N) by indexing listTwo by ids.
Case 1 - assuming listTwo has no objects with the same id
// pair each id with its marks
Map<String, String> marksIndex = listTwo.stream().collect(Collectors.toMap(ObjectTwo::getId, ObjectTwo::getMarks));
// go through list of `ObjectOne`s and lookup marks in the index
listOne.forEach(o1 -> o1.setScore(marksIndex.get(o1.getId())));
Case 2 - assuming listTwo has objects with the same id
final Map<String, List<ObjectTwo>> marksIndex = listTwo.stream()
.collect(Collectors.groupingBy(ObjectTwo::getId, Collectors.toList()));
final List<ObjectOne> result = listOne.stream()
.flatMap(o1 -> marksIndex.get(o1.getId()).stream().map(o2 -> {
// make a copy of ObjectOne instance to avoid overwriting scores
ObjectOne copy = copy(o1);
copy.setScore(o2.getMarks());
return copy;
}))
.collect(Collectors.toList());
To implement copy method you either need to create a new object and copy fields one by one, but in such cases I prefer to follow the Builder pattern. It also results in more "functional" code.
Following code copies marks from ObjectTwo to score in ObjectOne, if both ids are equal, it doesn't have intermediate object List<ObjectOne> result
listOne.stream()
.forEach(one -> {listTwo.stream()
.filter(two -> {return two.getId().equals(one.getId());})
.limit(1)
.forEach(two -> {one.setScore(two.getMarks());});
});
Your immediate problem is trivial to fix:
Optional<Integer> oi = nums.stream()
.filter(x -> x > nextNumber)
.findFirst();
System.out.println(oi.isPresent()? "Found: "+oi.get() : "Not found");
However, if you want to write code which optimally computes what you require it to, it is not the right approach. A far better option would be this:
OptionalInt oi = Stream.of(scn.nextLine().split(" "))
.mapToInt(Integer::parseInt)
.filter(i -> i > nextNumber)
.min();
System.out.println(oi.isPresent()? "Found: "+oi.getAsInt() : "Not found");
The advantage is that you never get an ArrayList involved and don't need to autobox the integers at any step, and you actually retrieve the smallest number satisfying the criterion, not the first one.
If you want to find the smallest number larger than some bound:
private int smallestLargerThan(int x, List<Integer> list) {
return list.stream().filter(n -> n > x).mapToInt(n -> n).min();
}
.filter(n -> n > x) drops all values less than or equal to x.
.mapToInt(n -> n) transforms the Stream to an IntStream, which is required for the next operation:
.min() returns the smallest element in the IntStream. Since the stream at this point only contains values greater than x, the returned element is the number you are looking for.
The code you posted won't compile because .findFirst() returns an Optional<Integer>, not an Integer. It is also semantically wrong because the first element is not necessarily the smallest as your Stream is unsorted.
You don't need to compare
List<Integer> c = new ArrayList<>(a);
c.removeAll(b);
And if you don't mind loosing the original list data
a.removeAll(b);
Something like this should suffice:
Set<Integer> container = new HashSet<>(ListB);
ListA.stream()
.filter(id -> !container.contains(id))
.forEach(System.out::println);
or non-stream:
Set<Integer> container = new HashSet<>(ListB);
for(Integer id : ListA)
if(!container.contains(id));
System.out.println(id);