How to simplify comparing? - java

I do have list like this:
List<Integer> list = new ArrayList<>();
list.add(24);
list.add(12);
list.add(0);
list.add(36);
list.add(1);
list.add(99);
I want to sort it ascendig, but 0 has to always be on the last one. Is there any way to simplify it?
List<Integer> collect = list.stream()
.sorted((a,b) -> b.compareTo(a))
.sorted((a, b) -> Integer.valueOf(0).equals(a) ? 1 : -1)
.collect(Collectors.toList());

List<Integer> list = List.of(24, 12, 0, 36, 1, 99);
List<Integer> sorted = list.stream()
.sorted(Comparator.comparingInt(a -> a == 0 ? Integer.MAX_VALUE : a))
.toList();
System.out.println(sorted);
Seems to work; prints [1, 12, 24, 36, 99, 0].
The one downside is that it won't do the right thing if literally the maximum integer value (which is 2147483647) is in your list, in which case it'll sort the 0s amongst them instead of after them. If that is a problem, nothing is going to look significantly shorter than what you did.
NB: Your code appears to sort descending. In which case the 0 would already sort at the end unless you have negative numbers. If that's what you want and 'ascending' was a typo, you'd have to use MIN_VALUE instead, and reverse the comparator (tack .reverse() t the end).

You can process in the same sorted:
List<Integer> collect = list.stream()
.sorted((o1, o2) -> o1 == 0 ? 1 : (o2 == 0 ? -1 : o1.compareTo(o2)))
.collect(Collectors.toList());

May be introducing variables for the comparators and comparing the boolean if an int equals to zero might simplify or at least make it readable
Comparator<Integer> natural = Comparator.naturalOrder();
Comparator<Integer> zeroLast = Comparator.comparing(i -> i.equals(0));
List<Integer> collect = list.stream()
.sorted(natural)
.sorted(zeroLast)
.collect(Collectors.toList());

Related

java: implementing vector addition with streams

Is there an elegant way to interoperate two streams?
/**
* e.g. add([1, -1, 5], [2, 3, 4]) -> [3, 2, 9]
*/
Stream<Integer> add(Stream<Integer> a, Stream<Integer> b) {
//?
}
I would hate to collect both streams to do the thing in a for-loop, just to stream the result again
Stream<Integer> add(Stream<Integer> a, Stream<Integer> b) {
Iterator<Integer> i=a.iterator();
return b.map(bz-> i.hasNext() ? bz + i.next() : bz);
}
I think this question really highlights why you probably shouldn't be doing what you're doing.
1st. You're adding three pairs of int's, and your using a Stream so you have to use Integer. That's huge overhead relative to the operation.
2nd. The concept of Stream is not limited to ordered sets of data.
For that reason collecting to a list makes sense because you're explicitly operating on a finite ordered dataset.
List<Integer> la = a.collect( Collectors.toList());
List<Integer> lb = b.collect( Collectors.toList());
Stream<Integer> result = IntStream.range( 0, la.size() ).mapToObj(
i -> la.get(i) && lb.get(j)
);
It might be more stream-like to use a.iterator() instead of lists because you wouldn't be limited to finite datasets.
Iterator<Integer> ia = a.iterator();
Iterator<Integer> ib = b.iterator();
if( ! ia.hasNext() || ! ib.hasNext() ) return Stream.empty();
return Stream.iterate(
ia.next() + ib.next(),
last -> ia.hasNext() && ib.hasNext(),
last -> ia.next() + ib.next() );

Filter List for unique elements

I am searching for an elegant way to filter a list for only the elements that are unique. An example:
[1, 2, 2, 3, 1, 4]
-> [3, 4] // 1 and 2 occur more than once
Most solutions I found manually compute the occurrences of all elements and then filter by the elements that have exactly one occurrence.
That does not sound too elegant to me, maybe there is a better solution, a best practice or a name for a data-structure that solves this already? I was also thinking about maybe utilizing streams, but I do not know how.
Note that I am not asking for duplicate removal, i.e. [1, 2, 3, 4] but for keeping only the unique elements, so [3, 4].
The order of the resulting list or what type of Collection exactly does not matter to me.
I doubt there is a better approach than actually counting and filtering for the ones that only appeared once. At least, all approaches I can think of will use something similar to that under the hood.
Also, it is not clear what you mean by elegant, readability or performance? So I will just dump some approaches.
Stream counting
Here is a stream-variant that computes number of occurrences (Map) and then filters for elements that appear only once. It is essentially the same as what you described already, or what Bags do under the hood:
List<E> result = elements.stream() // Stream<E>
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) // Map<E, Long>
.entries() // Set<Entry<E, Long>>
.stream() // Stream<Entry<E, Long>>
.filter(entry -> entry.getValue() == 1)
.map(Entry::getKey)
.collect(Collectors.toList());
It requires two full iterations over the data-set. Since it uses the Stream-API, the operations support multi-threading right from the get-go though. So if you have lots of elements, this might be pretty fast due to that.
Manual Set
Here is another approach that reduces iteration and lookup time by manually collecting into a Set to identify duplicates as fast as possible:
Set<E> result = new HashSet<>();
Set<E> appeared = new HashSet<>();
for (E element : elements) {
if (result.contains(element)) { // 2nd occurrence
result.remove(element);
appeared.add(element);
continue;
}
if (appeared.contains(element)) { // >2nd occurrence
continue;
}
result.add(element); // 1st occurrence
}
As you see, this only requires one iteration over the elements instead of multiple.
This approach is elegant in a sense that it does not compute unnecessary information. For what you want, it is completely irrelevant to compute how often exactly elements appear. We only care for "does it appear once or more often?" and not if it appears 5 times or 11 times.
You can use Bag to count occurrences (getCount(1) for unique)
Bag is a collection that allows storing multiple items along with their repetition count:
public void whenAdded_thenCountIsKept() {
Bag<Integer> bag = new HashBag<>(
Arrays.asList(1, 2, 3, 3, 3, 1, 4));
assertThat(2, equalTo(bag.getCount(1)));
}
Or CollectionBag
Apache Collections' library provides a decorator called the CollectionBag. We can use this to make our bag collections compliant with the Java Collection contract:
And get unique set:
bag.uniqueSet();
Returns a Set of unique elements in the Bag.
One need first to collect all, reached the end for deleting groups of more than 1 element.
Map<String, Long> map = Stream.of("a", "b", "a", "a", "c", "d", "c")
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
map.entrySet()
.stream()
.filter(e -> e.getValue() == 1L)
.map(e -> e.getKey())
.forEach(System.out::println);
Or in one go:
Stream.of("a", "b", "a", "a", "c", "d", "c")
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet()
.stream()
.filter(e -> e.getValue() == 1L)
.map(e -> e.getKey())
.forEach(System.out::println);
The idea of using a map to accumulate frequency counts sounds like a good one: it runs in roughly linear (O(n)) time and only requires O(n) extra space.
Here's an algorithm that requires zero extra space, at the expense of running in O(n^2) time:
public static <T> void retainSingletons(List<T> list)
{
int i = 0;
while (i < list.size()) {
boolean foundDup = false;
int j = i + 1;
while (j < list.size()) {
if (list.get(i).equals(list.get(j))) {
list.remove(j);
foundDup = true;
} else {
++j;
}
}
if (foundDup) {
list.remove(i);
} else {
++i;
}
}
}
The idea is straightforward: step a slow pointer, i, over the list until it runs off the end; for each value of i, run a fast pointer j from i+1 until the end of the list, removing any list[j] that's a duplicate of list[i]; after j runs out, if any duplicates of list[i] were found and removed, also remove list[i].
The following will work using Eclipse Collections:
IntList list = IntLists.mutable.with(1, 2, 2, 3, 1, 4);
IntSet unique = list.toBag().selectUnique();
System.out.println(unique);
Using an IntList removes the need to box the int values and Integer objects.
Note: I am a committer for Eclipse Collections.

Java 8 Streams difference between map and filter

Result of both approach are same which is best practise to use. I am new bee to java 8. i am little bit confused on stream.map and stream.filter
List<String> alpha =
Arrays.asList("a", "b", "csddddddddddd",
"d", "ssdddddddddd", "sw", "we", "wew");
// Java 8
List<String> collect = alpha.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
List<Integer> collect2 = alpha.stream()
.map(s -> s.length())
.collect(Collectors.toList());
List<Integer> collect3 = collect2.stream()
.filter(s -> s > 10)
.collect(Collectors.toList());
List<Integer> collect4 = collect2.stream()
.map(s -> {
Integer temp = 0;
if (s > 10) {
temp = s;
}
return temp;
})
.filter(s -> s > 10)
.collect(Collectors.toList());
Result of both List are same :
[13, 12]
[13, 12]
what is best approach. and what is best approach in this regards.
If you want to filter the elements of a Stream by some condition (i.e. remove elements that don't satisfy the condition), use filter, not map.
The purpose of map is to convert an element of one type to an element of another type.
The only reason you get the same results in collect3 and collect4 is that after applying map in collect4, you apply a filter, which removes all the 0s produced by map.
The entire .map(s ->{ Integer temp = 0;if(s>10) {temp=s;} return temp;}) call is redundant. That's a very unreadable and inefficient way to write code.

Is [var = modify(var)] a bad practice?

As a simplified example, given an array of numbers. I need to apply some value filters.
So what I'm doing currently:
Original given array: List<Integer>originalList;
How I construct my filtering methods:
List<Integer> removeNegativeNumber(final List<Integer> numberList) {
return FluentIterable.from(numberList).filter(rule).toList();
}
How I use:
List<Integer> filteredList = removeNegativeNumber(originalList);
filteredList = removeOddNumber(filteredList);
filteredList = removeZeroNumber(filteredList);
My feeling tells me that it can be done better. But I don't know how. Can someone give me advises, recommendations for improving my code ?
Consider lambdas and Stream#filter() if you're using Java 8:
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5);
List<Integer> filteredList = list.stream()
.filter(i -> i % 2 == 0)
.filter(i -> i != 0)
.collect(Collectors.toList());
System.out.println(filteredList);
Or using IntStream:
IntStream stream = IntStream.of(0, 1, 2, 3, 4, 5);
IntStream filteredStream = stream.filter(i -> i % 2 == 0)
.filter(i -> i != 0);
filteredStream.forEach(System.out::println);
Javadoc:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#filter-java.util.function.Predicate-
If you don't need to retain neither the original nor transitional values, you could use methods with side-effects rather than a pure function :
removeNegativeNumber(theOnlyList);
removeOddNumber(theOnlyList);
removeZeroNumber(theOnlyList);
// now the list only contains positive non-zero even numbers
These methods could be implemented by using an Iterator's remove() method when its current element does not satisfy the conditions.
If you need to retain the original values, but not the transitional values, you should use pure functions and link them as described by 4castle. As an addition, a more modern way to do so would be to use the Stream API :
List<Integer> filteredList = originalList.stream()
.filter(i -> i >= 0)
.filter(i -> i % 2 == 0)
.filter(i -> i != 0)
.collect(Collectors.toList());
You could chain the statements together, such as like this:
List<Integer> filteredList = removeZeroNumber(
removeOddNumber(
removeNegativeNumber(originalList)));
But that is wasteful because it maps back and forth from a List to FluentIterable with each method. You're better off chaining the underlying FluentIterable methods:
List<Integer> filteredList = FluentIterable.from(numberList)
.filter(i -> i > 0)
.filter(i -> i % 2 == 0)
.toList();
If you're using Java 8 though, an IntStream should be used to prevent repeated autoboxing:
List<Integer> filteredList = originalList.stream()
.mapToInt(Integer::intValue)
.filter(i -> i > 0)
.filter(i -> i % 2 == 0)
.boxed()
.collect(Collectors.toList());

How to force max to return ALL maximum values in a Java Stream?

I've tested a bit the max function on Java 8 lambdas and streams, and it seems that in case max is executed, even if more than one object compares to 0, it returns an arbitrary element within the tied candidates without further consideration.
Is there an evident trick or function for such a max expected behavior, so that all max values are returned? I don't see anything in the API but I am sure it must exist something better than comparing manually.
For instance:
// myComparator is an IntegerComparator
Stream.of(1, 3, 5, 3, 2, 3, 5)
.max(myComparator)
.forEach(System.out::println);
// Would print 5, 5 in any order.
I believe the OP is using a Comparator to partition the input into equivalence classes, and the desired result is a list of members of the equivalence class that is the maximum according to that Comparator.
Unfortunately, using int values as a sample problem is a terrible example. All equal int values are fungible, so there is no notion of preserving the ordering of equivalent values. Perhaps a better example is using string lengths, where the desired result is to return a list of strings from an input that all have the longest length within that input.
I don't know of any way to do this without storing at least partial results in a collection.
Given an input collection, say
List<String> list = ... ;
...it's simple enough to do this in two passes, the first to get the longest length, and the second to filter the strings that have that length:
int longest = list.stream()
.mapToInt(String::length)
.max()
.orElse(-1);
List<String> result = list.stream()
.filter(s -> s.length() == longest)
.collect(toList());
If the input is a stream, which cannot be traversed more than once, it is possible to compute the result in only a single pass using a collector. Writing such a collector isn't difficult, but it is a bit tedious as there are several cases to be handled. A helper function that generates such a collector, given a Comparator, is as follows:
static <T> Collector<T,?,List<T>> maxList(Comparator<? super T> comp) {
return Collector.of(
ArrayList::new,
(list, t) -> {
int c;
if (list.isEmpty() || (c = comp.compare(t, list.get(0))) == 0) {
list.add(t);
} else if (c > 0) {
list.clear();
list.add(t);
}
},
(list1, list2) -> {
if (list1.isEmpty()) {
return list2;
}
if (list2.isEmpty()) {
return list1;
}
int r = comp.compare(list1.get(0), list2.get(0));
if (r < 0) {
return list2;
} else if (r > 0) {
return list1;
} else {
list1.addAll(list2);
return list1;
}
});
}
This stores intermediate results in an ArrayList. The invariant is that all elements within any such list are equivalent in terms of the Comparator. When adding an element, if it's less than the elements in the list, it's ignored; if it's equal, it's added; and if it's greater, the list is emptied and the new element is added. Merging isn't too difficult either: the list with the greater elements is returned, but if their elements are equal the lists are appended.
Given an input stream, this is pretty easy to use:
Stream<String> input = ... ;
List<String> result = input.collect(maxList(comparing(String::length)));
I would group by value and store the values into a TreeMap in order to have my values sorted, then I would get the max value by getting the last entry as next:
Stream.of(1, 3, 5, 3, 2, 3, 5)
.collect(groupingBy(Function.identity(), TreeMap::new, toList()))
.lastEntry()
.getValue()
.forEach(System.out::println);
Output:
5
5
I implemented more generic collector solution with custom downstream collector. Probably some readers might find it useful:
public static <T, A, D> Collector<T, ?, D> maxAll(Comparator<? super T> comparator,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BinaryOperator<A> downstreamCombiner = downstream.combiner();
class Container {
A acc;
T obj;
boolean hasAny;
Container(A acc) {
this.acc = acc;
}
}
Supplier<Container> supplier = () -> new Container(downstreamSupplier.get());
BiConsumer<Container, T> accumulator = (acc, t) -> {
if(!acc.hasAny) {
downstreamAccumulator.accept(acc.acc, t);
acc.obj = t;
acc.hasAny = true;
} else {
int cmp = comparator.compare(t, acc.obj);
if (cmp > 0) {
acc.acc = downstreamSupplier.get();
acc.obj = t;
}
if (cmp >= 0)
downstreamAccumulator.accept(acc.acc, t);
}
};
BinaryOperator<Container> combiner = (acc1, acc2) -> {
if (!acc2.hasAny) {
return acc1;
}
if (!acc1.hasAny) {
return acc2;
}
int cmp = comparator.compare(acc1.obj, acc2.obj);
if (cmp > 0) {
return acc1;
}
if (cmp < 0) {
return acc2;
}
acc1.acc = downstreamCombiner.apply(acc1.acc, acc2.acc);
return acc1;
};
Function<Container, D> finisher = acc -> downstream.finisher().apply(acc.acc);
return Collector.of(supplier, accumulator, combiner, finisher);
}
So by default it can be collected to a list using:
public static <T> Collector<T, ?, List<T>> maxAll(Comparator<? super T> comparator) {
return maxAll(comparator, Collectors.toList());
}
But you can use other downstream collectors as well:
public static String joinLongestStrings(Collection<String> input) {
return input.stream().collect(
maxAll(Comparator.comparingInt(String::length), Collectors.joining(","))));
}
If I understood well, you want the frequency of the max value in the Stream.
One way to achieve that would be to store the results in a TreeMap<Integer, List<Integer> when you collect elements from the Stream. Then you grab the last key (or first depending on the comparator you give) to get the value which will contains the list of max values.
List<Integer> maxValues = st.collect(toMap(i -> i,
Arrays::asList,
(l1, l2) -> Stream.concat(l1.stream(), l2.stream()).collect(toList()),
TreeMap::new))
.lastEntry()
.getValue();
Collecting it from the Stream(4, 5, -2, 5, 5) will give you a List [5, 5, 5].
Another approach in the same spirit would be to use a group by operation combined with the counting() collector:
Entry<Integer, Long> maxValues = st.collect(groupingBy(i -> i,
TreeMap::new,
counting())).lastEntry(); //5=3 -> 5 appears 3 times
Basically you firstly get a Map<Integer, List<Integer>>. Then the downstream counting() collector will return the number of elements in each list mapped by its key resulting in a Map. From there you grab the max entry.
The first approaches require to store all the elements from the stream. The second one is better (see Holger's comment) as the intermediate List is not built. In both approached, the result is computed in a single pass.
If you get the source from a collection, you may want to use Collections.max one time to find the maximum value followed by Collections.frequency to find how many times this value appears.
It requires two passes but uses less memory as you don't have to build the data-structure.
The stream equivalent would be coll.stream().max(...).get(...) followed by coll.stream().filter(...).count().
I'm not really sure whether you are trying to
(a) find the number of occurrences of the maximum item, or
(b) Find all the maximum values in the case of a Comparator that is not consistent with equals.
An example of (a) would be [1, 5, 4, 5, 1, 1] -> [5, 5].
An example of (b) would be:
Stream.of("Bar", "FOO", "foo", "BAR", "Foo")
.max((s, t) -> s.toLowerCase().compareTo(t.toLowerCase()));
which you want to give [Foo, foo, Foo], rather than just FOO or Optional[FOO].
In both cases, there are clever ways to do it in just one pass. But these approaches are of dubious value because you would need to keep track of unnecessary information along the way. For example, if you start with [2, 0, 2, 2, 1, 6, 2], it would only be when you reach 6 that you would realise it was not necessary to track all the 2s.
I think the best approach is the obvious one; use max, and then iterate the items again putting all the ties into a collection of your choice. This will work for both (a) and (b).
If you'd rather rely on a library than the other answers here, StreamEx has a collector to do this.
Stream.of(1, 3, 5, 3, 2, 3, 5)
.collect(MoreCollectors.maxAll())
.forEach(System.out::println);
There's a version which takes a Comparator too for streams of items which don't have a natural ordering (i.e. don't implement Comparable).
System.out.println(
Stream.of(1, 3, 5, 3, 2, 3, 5)
.map(a->new Integer[]{a})
.reduce((a,b)->
a[0]==b[0]?
Stream.concat(Stream.of(a),Stream.of(b)).toArray() :
a[0]>b[0]? a:b
).get()
)
I was searching for a good answer on this question, but a tad more complex and couldn't find anything until I figured it out myself, which is why I'm posting if this helps anybody.
I have a list of Kittens.
Kitten is an object which has a name, age and gender. I had to return a list of all the youngest kittens.
For example:
So kitten list would contain kitten objects (k1, k2, k3, k4) and their ages would be (1, 2, 3, 1) accordingly. We want to return [k1, k4], because they are both the youngest. If only one youngest exists, the function should return [k1(youngest)].
Find the min value of the list (if it exists):
Optional<Kitten> minKitten = kittens.stream().min(Comparator.comparingInt(Kitten::getAge));
filter the list by the min value
return minKitten.map(value -> kittens.stream().filter(kitten -> kitten.getAge() == value.getAge())
.collect(Collectors.toList())).orElse(Collections.emptyList());
The following two lines will do it without implementing a separate comparator:
List<Integer> list = List.of(1, 3, 5, 3, 2, 3, 5);
list.stream().filter(i -> i == (list.stream().max(Comparator.comparingInt(i2 -> i2))).get()).forEach(System.out::println);

Categories