Related
I have the following code:
private List<String> validate(StartValue start, List<String> colors, Entity entity) {
if (!CollectionUtils.isEmpty(colors)) {
return colors.stream()
.filter(color -> ValidationUtil.getColorfulValues(start, color.getRGBValue()).isEmpty() ||
(!ValidationUtil.getColorfulValues(start, color.getRGBValue()).isEmpty() &&
ValidationUtil.getColorfulValues(start, color.getRGBValue()).contains(entity.getColor())))
.collect(Collectors.toList());
}
return colors;
}
Here ValidationUtil.getColorfulValues is getting called thrice for each value in the list. Is there a way to optimize the filter so that we can save the value of the call?
If you would have not represented the code as a lambda expression, but as a block statement you would have been able to simplify it further.
.filter(color -> {
List<String> colourFulValues = ValidationUtil.getColorfulValues(start,
color.getRGBValue());
return colourFulValues.isEmpty() || colourFulValues.contains(entity.getColor())
}
Of course, you could abstract the block as a Predicate of its own depending on its usage. The type of the Predicate would be the type you have chosen to represent the color. Currently, in your question, for example, if it's a java.lang.String, where is the method color.getRGBValue() associated from?
You can use a method reference:
private List<String> validate(StartValue start, List<String> colors, Entity entity) {
if (!CollectionUtils.isEmpty(colors)) {
return colors.stream()
.filter(this::filter)
.collect(Collectors.toList());
}
return colors;
}
private boolean filter(String color) {
var rgbVal = color.getRGBValue();
var cv = ValidationUtil.getColorfulValues(start, rgbVal);
boolean empty = cv.isEmpty();
return empty || (!empty && cv.contains(entity.getColor()));
}
Note that I use var since it is not clear to me what color.getRGBValue() returns and also what ValidationUtil.getColorfulValues() returns.
Guessing that CollectionUtils.isEmpty considers null elements non-existing.
For the filter condition evaluating ValidationUtil.getColorfulValues(start, color.getRGBValue())) only once requires mapping to it. But one needs to conserve the color too, to collect it in a list. So I introduced a record ColorWithValues. The record class is intended for such kind of uses.
|| is a short-circuit operator and does not need a negation of its first argument.
So:
private List<String> validate(StartValue start, List<String> colors, Entity entity) {
record ColorWithValues(String color, Set<String> values) {}
return colors.stream()
.filter(Objects::nonNull)
.map(c ->
new ColorWithValues(c,
ValidationUtil.getColorfulValues(start, c.getRGBValue())))
.filter(cv -> cv.values.isEmpty() ||
cv.values.contains(entity.getColor()))
.map(cv -> cv.color)
.collect(Collectors.toList());
}
I am having the below code
trainResponse.getIds().stream()
.filter(id -> id.getType().equalsIgnoreCase("Company"))
.findFirst()
.ifPresent(id -> {
domainResp.setId(id.getId());
});
trainResponse.getIds().stream()
.filter(id -> id.getType().equalsIgnoreCase("Private"))
.findFirst()
.ifPresent(id ->
domainResp.setPrivateId(id.getId())
);
Here I'm iterating/streaming the list of Id objects 2 times.
The only difference between the two streams is in the filter() operation.
How to achieve it in single iteration, and what is the best approach (in terms of time and space complexity) to do this?
You can achieve that with Stream IPA in one pass though the given set of data and without increasing memory consumption (i.e. the result will contain only ids having required attributes).
For that, you can create a custom Collector that will expect as its parameters a Collection attributes to look for and a Function responsible for extracting the attribute from the stream element.
That's how this generic collector could be implemented.
/** *
* #param <T> - the type of stream elements
* #param <F> - the type of the key (a field of the stream element)
*/
class CollectByKey<T, F> implements Collector<T, Map<F, T>, Map<F, T>> {
private final Set<F> keys;
private final Function<T, F> keyExtractor;
public CollectByKey(Collection<F> keys, Function<T, F> keyExtractor) {
this.keys = new HashSet<>(keys);
this.keyExtractor = keyExtractor;
}
#Override
public Supplier<Map<F, T>> supplier() {
return HashMap::new;
}
#Override
public BiConsumer<Map<F, T>, T> accumulator() {
return this::tryAdd;
}
private void tryAdd(Map<F, T> map, T item) {
F key = keyExtractor.apply(item);
if (keys.remove(key)) {
map.put(key, item);
}
}
#Override
public BinaryOperator<Map<F, T>> combiner() {
return this::tryCombine;
}
private Map<F, T> tryCombine(Map<F, T> left, Map<F, T> right) {
right.forEach(left::putIfAbsent);
return left;
}
#Override
public Function<Map<F, T>, Map<F, T>> finisher() {
return Function.identity();
}
#Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}
main() - demo (dummy Id class is not shown)
public class CustomCollectorByGivenAttributes {
public static void main(String[] args) {
List<Id> ids = List.of(new Id(1, "Company"), new Id(2, "Fizz"),
new Id(3, "Private"), new Id(4, "Buzz"));
Map<String, Id> idByType = ids.stream()
.collect(new CollectByKey<>(List.of("Company", "Private"), Id::getType));
idByType.forEach((k, v) -> {
if (k.equalsIgnoreCase("Company")) domainResp.setId(v);
if (k.equalsIgnoreCase("Private")) domainResp.setPrivateId(v);
});
System.out.println(idByType.keySet()); // printing keys - added for demo purposes
}
}
Output
[Company, Private]
Note, after the set of keys becomes empty (i.e. all resulting data has been fetched) the further elements of the stream will get ignored, but still all remained data is required to be processed.
IMO, the two streams solution is the most readable. And it may even be the most efficient solution using streams.
IMO, the best way to avoid multiple streams is to use a classical loop. For example:
// There may be bugs ...
boolean seenCompany = false;
boolean seenPrivate = false;
for (Id id: getIds()) {
if (!seenCompany && id.getType().equalsIgnoreCase("Company")) {
domainResp.setId(id.getId());
seenCompany = true;
} else if (!seenPrivate && id.getType().equalsIgnoreCase("Private")) {
domainResp.setPrivateId(id.getId());
seenPrivate = true;
}
if (seenCompany && seenPrivate) {
break;
}
}
It is unclear whether that is more efficient to performing one iteration or two iterations. It will depend on the class returned by getIds() and the code of iteration.
The complicated stuff with two flags is how you replicate the short circuiting behavior of findFirst() in your 2 stream solution. I don't know if it is possible to do that at all using one stream. If you can, it will involve something pretty cunning code.
But as you can see your original solution with 2 stream is clearly easier to understand than the above.
The main point of using streams is to make your code simpler. It is not about efficiency. When you try to do complicated things to make the streams more efficient, you are probably defeating the (true) purpose of using streams in the first place.
For your list of ids, you could just use a map, then assign them after retrieving, if present.
Map<String, Integer> seen = new HashMap<>();
for (Id id : ids) {
if (seen.size() == 2) {
break;
}
seen.computeIfAbsent(id.getType().toLowerCase(), v->id.getId());
}
If you want to test it, you can use the following:
record Id(String getType, int getId) {
#Override
public String toString() {
return String.format("[%s,%s]", getType, getId);
}
}
Random r = new Random();
List<Id> ids = r.ints(20, 1, 100)
.mapToObj(id -> new Id(
r.nextBoolean() ? "Company" : "Private", id))
.toList();
Edited to allow only certain types to be checked
If you have more than two types but only want to check on certain ones, you can do it as follows.
the process is the same except you have a Set of allowed types.
You simply check to see that your are processing one of those types by using contains.
Map<String, Integer> seen = new HashMap<>();
Set<String> allowedTypes = Set.of("company", "private");
for (Id id : ids) {
String type = id.getType();
if (allowedTypes.contains(type.toLowerCase())) {
if (seen.size() == allowedTypes.size()) {
break;
}
seen.computeIfAbsent(type,
v -> id.getId());
}
}
Testing is similar except that additional types need to be included.
create a list of some types that could be present.
and build a list of them as before.
notice that the size of allowed types replaces the value 2 to permit more than two types to be checked before exiting the loop.
List<String> possibleTypes =
List.of("Company", "Type1", "Private", "Type2");
Random r = new Random();
List<Id> ids =
r.ints(30, 1, 100)
.mapToObj(id -> new Id(possibleTypes.get(
r.nextInt((possibleTypes.size()))),
id))
.toList();
You can group by type and check the resulting map.
I suppose the type of ids is IdType.
Map<String, List<IdType>> map = trainResponse.getIds()
.stream()
.collect(Collectors.groupingBy(
id -> id.getType().toLowerCase()));
Optional.ofNullable(map.get("company")).ifPresent(ids -> domainResp.setId(ids.get(0).getId()));
Optional.ofNullable(map.get("private")).ifPresent(ids -> domainResp.setPrivateId(ids.get(0).getId()));
I'd recommend a traditionnal for loop. In addition of being easily scalable, this prevents you from traversing the collection multiple times.
Your code looks like something that'll be generalised in the future, thus my generic approch.
Here's some pseudo code (with errors, just for the sake of illustration)
Set<String> matches = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
for(id : trainResponse.getIds()) {
if (! matches.add(id.getType())) {
continue;
}
switch (id.getType().toLowerCase()) {
case "company":
domainResp.setId(id.getId());
break;
case "private":
...
}
}
Something along these lines can might work, it would go through the whole stream though, and won't stop at the first occurrence.
But assuming a small stream and only one Id for each type, why not?
Map<String, Consumer<String>> setters = new HashMap<>();
setters.put("Company", domainResp::setId);
setters.put("Private", domainResp::setPrivateId);
trainResponse.getIds().forEach(id -> {
if (setters.containsKey(id.getType())) {
setters.get(id.getType()).accept(id.getId());
}
});
We can use the Collectors.filtering from Java 9 onwards to collect the values based on condition.
For this scenario, I have changed code like below
final Map<String, String> results = trainResponse.getIds()
.stream()
.collect(Collectors.filtering(
id -> id.getType().equals("Company") || id.getIdContext().equals("Private"),
Collectors.toMap(Id::getType, Id::getId, (first, second) -> first)));
And getting the id from results Map.
I have a list of valid values for a type:
Set<String> validTypes = ImmutableSet.of("TypeA", "TypeB", "TypeC");
From a given list I want to extract the first value which has a valid type. In this scenario I would write something of this sort:
public class A{
private String type;
private String member;
}
List<A> classAList;
classAList.stream()
.filter(a -> validTypes.contains(a.getType()))
.findFirst();
However I would like to give preference to TypeA, i.e. if classAList has TypeA and TypeB, I want the object which has typeA. To do this one approach I've is:
Set<String> preferredValidTypes = ImmutableSet.of("TypeA");
classAList.stream()
.filter(a -> preferredValidTypes.contains(a.getType()))
.findFirst()
.orElseGet(() -> {
return classAList.stream()
.filter(a -> validTypes.contains(a.getType()))
.findFirst();
}
Is there a better approach?
filter list by type, order by type, collect to list, then just get first element
List<A> collect = classAList.stream()
.filter(a -> validTypes.contains(a.getType()))
.sorted(Comparator.comparing(A::getType))
.collect(Collectors.toList());
System.out.println(collect.get(0));
You can use a custom comparator like:
Comparator<A> comparator = (o1, o2) -> {
if (preferredValidTypes.contains(o1.getType()) && !preferredValidTypes.contains(o2.getType())) {
return 1;
} else if (!preferredValidTypes.contains(o1.getType()) && preferredValidTypes.contains(o2.getType())) {
return -1;
} else {
return 0;
}
};
to sort the list and then findFirst from that list with your conditiion.
i don't like the answers already given which use Comparator. Sorting is an expensive operation. You can do it with one walk through the list. Once you find a preferred value, you can break out, otherwise you continue to the end to find a valid.
In this case anyMatch can provide the possibility to break out from the stream processing:
MyVerifier verifier=new MyVerifier(validTypes,preferredValidTypes);
classAList.stream()
.anyMatch(verifier);
System.out.println("Preferred found:"+verifier.preferred);
System.out.println("Valid found:"+verifier.valid);
public static class MyVerifier implements Predicate<A> {
private Set<String> validTypes;
private Set<String> preferredValidTypes;
A preferred=null;
A valid=null;
public MyVerifier(Set<String> validTypes, Set<String> preferredValidTypes) {
super();
this.validTypes = validTypes;
this.preferredValidTypes = preferredValidTypes;
}
#Override
public boolean test(A a) {
if(preferred==null && preferredValidTypes.contains(a.getType())) {
preferred=a;
// we can stop because we found the first preferred
return true;
} else if(valid==null && validTypes.contains(a.getType())) {
valid=a;
}
return false;
}
}
One can, of course, define two lists, one with all valid types, and one with the preferred types.
However, here is another approach. Define one list, or actually, a Map, with the keys being the valid types, and the boolean values being whether the type is preferred.
Map<String, Boolean> validTypes = ImmutableMap.of(
"TypeA", false,
"TypeB", false,
"TypeC", true
);
Using AtomicReference
One option is the following:
AtomicReference<A> ref = new AtomicReference<>();
listOfAs.stream()
.filter(t -> validTypes.containsKey(t.getType()))
.anyMatch(t -> validTypes.get(ref.updateAndGet(u -> t).getType()));
AtomicReference now contains a preferred A if available, or another valid A, or if the stream is empty, then it contains null. This stream operation short-circuits if an A with a preferred type is found.
The drawback of this option is that it creates side-effects, which is discouraged.
Using distinct()
Another suggestion would be the following. It uses the same map structure, using a boolean to indicate which values are preferred. However, it does not create side effects.
Map<Boolean, A> map = listOfAs.stream()
.filter(t -> validTypes.containsKey(t.getType()))
.map(t -> new Carrier<>(validTypes.get(t.getType()), t))
.distinct()
.limit(2)
.collect(Collectors.toMap(Carrier::getKey, Carrier::getValue));
It works as follows.
filter discards any element that is not a valid type.
Then, each element is mapped to a Carrier<Boolean, A> instance. A Carrier is a Map.Entry<K, V> which implements its equals and hashCode methods regarding only the key; the value does not matter. This is necessary for the following step,
distinct(), which discards any duplicate element. This way, only one preferred type and only one valid type is found.
We limit the stream to have 2 elements, one for each boolean. This is because the stream, which is lazy, stops evaluating after both booleans are found.
At last, we collect the Carrier elements into a Map.
The map contains now the following elements:
Boolean.TRUE => A with a preferred type
Boolean.FALSE => A with a valid type
Retrieve the appropriate element using
A a = map.getOrDefault(true, map.get(false)); // null if not found
Well you have to take care into account that sorting is stable, that is equal elements will appear in the same order as the initial source - and you need that to correctly get the first element from that List<A> that will satisfy your requirement, thus:
String priorityType = "TypeB";
Stream.of(new A("TypeA", "A"),
new A("TypeB", "B"),
new A("TypeC", "C"))
.sorted(Comparator.comparing(A::getType, Comparator.comparing(priorityType::equals)).reversed())
.filter(x -> validTypes.contains(priorityType))
.findFirst()
.orElseThrow(RuntimeException::new);
In Java8 you can use streams:
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsIn) {
return listCarnet.stream().filter(carnet -> codeIsIn.equals(carnet.getCodeIsin())).findFirst().orElse(null);
}
Additionally, in case you have many different objects (not only Carnet) or you want to find it by different properties (not only by cideIsin), you could build an utility class, to ecapsulate this logic in it:
public final class FindUtils {
public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(filter).findFirst().orElse(null);
}
}
public final class CarnetUtils {
public static Carnet findByCodeTitre(Collection<Carnet> listCarnet, String codeTitre) {
return FindUtils.findByProperty(listCarnet, carnet -> codeTitre.equals(carnet.getCodeTitre()));
}
public static Carnet findByNomTitre(Collection<Carnet> listCarnet, String nomTitre) {
return FindUtils.findByProperty(listCarnet, carnet -> nomTitre.equals(carnet.getNomTitre()));
}
public static Carnet findByCodeIsIn(Collection<Carnet> listCarnet, String codeIsin) {
return FindUtils.findByProperty(listCarnet, carnet -> codeIsin.equals(carnet.getCodeIsin()));
}
}
If you have preferred valid types in other collection so you can follow this code.
Map<String,A> groupByType = classAList
.stream()
/* additional filter to grouping by valid types.*/
//.filter(a->validTypes.contains(a.getType()))
.collect(Collectors.toMap(A::getType, Function.identity(),(v1, v2)->v1));
then use:
A result = preferredValidTypes
.stream()
.map(groupByType::get)
.findFirst()
.orElseThrow(RuntimeException::new);
or just group by preferred valid types
A result2 = classAList
.stream()
.filter(a -> preferredValidTypes.contains(a.getType()))
.collect(Collectors.toMap(A::getType, Function.identity(), (v1, v2) -> v1))
.entrySet()
.stream()
.findFirst()
.map(Map.Entry::getValue)
.orElseThrow(RuntimeException::new);
I have the following method:
public static List<A> getValuesExclusion(A exclusion) {
return Arrays.stream(values())
.filter(item -> item != exclusion)
.collect(Collectors.toList());
}
//this function returns enum list of A types that has no A type'exclusion'
Now I want to make it into a list as argument:
public static List<A> getValuesExclusion(A... exclusions){
return Arrays.stream(values())
.filter(???)
.collect(Collectors.toList());
}
My question is, how can I do the filter for the second case? I would like to retrieve an enum list that excludes all the values "exclusions" as input. Here are the attributes of class A:
public enum A implements multilingualA{
A("a"),
B("b"),
C("c"),
D("d");
...
}
If you want to make sure all the items are not included in the exclusions you could do:
public static List<A> getValuesExclusion(AType... exclusions){
return Arrays.stream(values())
.filter(e -> Arrays.stream(exclusions).noneMatch(c -> c == e))
.collect(Collectors.toList());
}
Which will create a Stream of exclusions and then use noneMatch() to ensure the given AType is not included in the Array
You should rethink whether List really is the appropriate data type for something containing unique elements. A Set usually is more appropriate.
Then, if you care for performance, you may implement it as
public static Set<A> getValuesExclusion(A... exclusions){
return exclusions.length == 0? EnumSet.allOf(A.class):
EnumSet.complementOf(EnumSet.of(exclusions[0], exclusions));
}
The class EnumSet is specifically designed for holding elements of an enum type, just storing a bit for each constant, to tell whether it is present or absent. This allows operations like complementOf, which just flips all bits using a single ⟨binary not⟩ operation, without the need to actually traverse the enum constants.
If you insist on returning a List, you can do it as easy as
public static List<A> getValuesExclusion(A... exclusions){
return new ArrayList<>(exclusions.length == 0? EnumSet.allOf(A.class):
EnumSet.complementOf(EnumSet.of(exclusions[0], exclusions)));
}
I would not go with Streams here but with the a (imho) more readable approach:
public static List<A> getValuesExclusion(AType... exclusions){
List<A> values = Arrays.asList(values());
values.removeAll(Arrays.asList(ex));
return values;
}
I have some Java code which filters a list based on some input. It currently uses a lambda, for example:
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects
.stream()
.filter( compObject -> allowedTags.contains(compObject.getTag()))
.collect(Collectors.toList());
}
What I want to do is to move the filter logic to another method to make it re-usable and easily unit testable. So I wanted to use a method reference in place of the lambda passed to the filter method. Easy to do if the filter logic is fairly static (i.e. list of allowed tags is known at compile time) but I can't figure out how to do this with dynamic data in the filter.
What I wanted was some way to use a method reference and then pass the second dynamic param i.e.
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects
.stream()
.filter(this::filterByAllowedTags, allowedTags)
.collect(Collectors.toList());
}
So is it possible to do what I want or am I possibly approaching this situation incorrectly?
I'd suggest passing in a Predicate as a parameter. That way the caller can filter based on any criteria it wants, including allowedTags or whatever:
public List<ComplexObject> retrieveObjectsFilteredBy(Predicate<ComplexObject> pred) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects.stream()
.filter(pred)
.collect(Collectors.toList());
}
This would be called like so:
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveObjectsFilteredBy(cobj -> allowedTags.contains(cobj.getTag()));
But you could go even further, depending on how much refactoring you're willing to do. Instead of "retrieve" returning a List, how about having it return a Stream? And instead of the retrieve-filter method returning a List, how about having it return a Stream too?
public Stream<ComplexObject> retrieveObjectsFilteredBy2(Predicate<ComplexObject> pred) {
Stream<ComplexObject> complexObjects = retrieveAllComplexObjects2();
return complexObjects.filter(pred);
}
And the calling side would look like this:
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveObjectsFilteredBy2(cobj -> allowedTags.contains(cobj.getTag()))
.collect(toList());
Now if you look at it carefully, you can see that the retrieve-filter method isn't adding any value at all, so you might just as well inline it into the caller:
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveAllComplexObjects2()
.filter(cobj -> allowedTags.contains(cobj.getTag()))
.collect(toList());
Of course, depending upon what the caller wants to do, it might not want to collect the results into a list; it might want to process the results with forEach(), or something else.
Now you can still factor out the filter into its own method, for testing/debugging, and you can use a method reference:
boolean cobjFilter(ComplexObject cobj) {
List<String> allowedTags = ... ;
return allowedTags.contains(cobj.getTag());
}
List<ComplexObject> result =
retrieveAllComplexObjects2()
.filter(this::cobjFilter)
.collect(toList());
If you don't want the filter to have the allowed tags built into it, you can change it from being a predicate into a higher-order function that returns a predicate instead:
Predicate<ComplexObject> cobjFilter(List<String> allowedTags) {
return cobj -> allowedTags.contains(cobj.getTag());
}
List<String> allowedTags = ... ;
List<ComplexObject> result =
retrieveAllComplexObjects2()
.filter(cobjFilter(allowedTags))
.collect(toList());
Which of these variations makes the most sense depends on what your application looks like and what kind of dynamicism you require in filtering.
How about the following? It extracts the predicate in a separate method to make it easily testable, and can be reused easily.
public Predicate<ComplexObject> tagAllowed(List<String> allowedTags) {
return (ComplexObject co) -> allowedTags.contains(co.getTag());
}
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
List<ComplexObject> complexObjects = retrieveAllComplexObjects();
return complexObjects
.stream()
.filter(tagAllowed(allowedTags))
.collect(Collectors.toList());
}
The problem with the method reference this::filterByAllowedTags is that it has the shape:
(ComplexObject, List<String>) -> boolean
But it's being passed into filter() which expects a lambda of the shape:
(ComplexObject) -> boolean
In other words, this::filterByAllowedTags can never be a Predicate, but it could be some alternate interface Predicate2. Then you'd also need an overload of filter which takes a Predicate2.
Eclipse Collections (formerly GS Collections) has the method select() which behaves just like filter(), and selectWith() which behaves like the overload I just described.
Using select():
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
// retrieveAllComplexObjects returns a MutableList, possibly FastList
MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
// select() returns MutableList here which extends List
return complexObjects.select(compObject -> allowedTags.contains(compObject.getTag()));
}
Using selectWith():
public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
// retrieveAllComplexObjects returns a MutableList, possibly FastList
MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
// select() returns MutableList here which extends List
return complexObjects.selectWith(this::filterByAllowedTags, allowedTags);
}
private boolean filterByAllowedTags(ComplexObject complexObject, List<String> allowedTags)
{
return allowedTags.contains(complexObject.getTag());
}
Note: I am a committer for Eclipse Collections.