3

pojoMappedByName contains pojotest and many other elements. How do I check if pojoTest is in pojoMappedByName ?

I treid using if(pojoMappedByName.containsValue(pojotest)) but it is returning false. Is it because the type is Optional<PojoClass> and not of Type PojoClass

PojoClass pojotest = new PojoClass();
PojoClass.setName("Tommy");

Map<String, Optional<PojoClass>> pojoMappedByName;

I want to avoid using for loops if possible

4 Answers 4

2

Optional shouldn't be there. Optional is optimally used as the return type of a terminal stream operation. It is somewhat controversial, but possibly acceptable1, to use it as the return type of non-stream-related methods. It's flat out a mistake to use it anywhere else - it should not be the type of a field, it should not be the type of a method parameter, and it should not show up in any generic type. List<Optional<String>>, etcetera - don't have those.

You've stumbled on a practical example of why that is. To generalize the principle: Optional isn't part of the type system the way generics is, and is orthogonal relative to the type you're 'wrapped'. In other words, String and Optional<String> are as different as guns and grandmas are, and that's a problem because in your head they feel similar-ish. Even invoking .equals on an optional is itself dubious, as Optional is attempting to catch the semantic meaning of 'I convey the result of a particular invocation of a stream terminal', and we can hold a long philosophical debate as to whether the concept of 'the way this terminal ended' can ever even be considered as equal to a different run that so happened to produce the same object as result.

Generics has 4 different notions on any particular type:

  • List<Number>
  • List<? extends Number>
  • List<? super Number>
  • List (raw / legacy)

proper integration of optional in the type system requires a similar treatment: For example, you'd want the ability to write a method that accepts either a List<Optional<String>> or a List<String>. If the method always treats the elements in the string as optional (as in, checks for NONE, etc), but if it writes to the list, only writes actual Strings (and never Optional.NONE), then this method can process either form.

This is analogous to how a List<? super Number> can call .add(Number) and yet if it calls .get(0), that is of type Object and not Number.

Java does not support this.

There is no way to write such a method.

Hence, do not integrate optional - given that you can't write that method, you don't want List<Optional<String>> anywhere in your codebase. Instead, consider Optional as a fleeting data type: If you get an Optional from anywhere, 'unroll' that optional on the spot. Do not store it in a field, do not pass it to another method. The only slightly less fleeting notion you should be considering is to return it verbatim, putting the onus of unwrapping that optional on your caller. That is acceptable.


If you want to compound this mistake and make your code base worse, this WILL work. But I strongly advise against it!

pojoMappedByName.containsValue(Optional.of(pojotest))

(Optional's equals method is defined as: NONE is equal to NONE, and any 2 SOMEs are equal to each other if a.equals(b) holds, where a and b are the actual objects contained within the two optionals, which is why that works. Doesn't make it a good idea though, that's more of a 'its the least worst option amongst all available interpretations of what equality might mean between optionals'.

[1] A lot of methods in java core and major libraries just do not work that way. For example, if you consider Optional<T> to be the de-facto correct thing to return for a method that can return the concept 'not relevant' or 'not found', then obviously java.util.Map's .get(key) method should be returning an Optional<V>. It doesn't, and never will as it wouldn't be a backwards compatible change. Given that there's a ton of code out there that thus cannot just shift paradigm and probably never will, there's a lot of resistance to the idea, and living in a world where any given method that can conceptually return 'not found', 'not available', etc - would then force you to first check the docs to see if returns T and null indicates not found, or if it returns Optional<T>. Forcing that kind of dichotomy on the ecosystem is, naturally, controversial. I haven't yet heard of a workable proposal to get to a place where Optional has replaced null for all method return values in all major libraries (and the core library). Until one exists, it'll remain controversial, and I'd strongly suggest you don't go there, and avoid using Optional<T> as return value for methods unless they are stream related. However, some libraries and a minority (but significant minority) of programming teams are doing this. In contrast to using Optional in e.g. List<Optional<T>> or as method parameter type: Only a very small amount of dev teams are doing this and no style guide suggests it.

Sign up to request clarification or add additional context in comments.

6 Comments

Very good Answer, except that there is nothing “dubious” about using Optional as a return type outside of streams. An Optional is appropriate anytime null would be a legitimate return value. As the Javadoc says, “Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors.”.
Yes, 'dubious' is the wrong word, I agree. I meant 'debatable', or at least 'controversial'. A ton of existing java infra just doesn't work that way (exhibit A: java.util.Map::get), and can't be changed without breaking backwards compatibility. I'll update the answer.
A very detailed explantation, much appreciated. I am using java 8 stream to get the map therefore the type is <String, Optional>
@CookieMonster You can use .filter(), .map(), or similar operation in between to unroll the optional in-stream.
@rzwitserloot I am using nested lists so I'm using flatMap and filter. Here is the link to the stream I'm using. I am open for any suggestions, any tips on how I can improve the stream without getting a optional type?
|
2

One possible solution would be to wrap it in an Optional like this:

 assertTrue(pojoMappedByName.containsValue(Optional.of(pojotest)));

Comments

0

You can get all the values of the HashMap , use stream filter operation to see if your searched pojo exists or not , use count to see the number of equals searched pojo and if zero then not exists else more than one pojo exists in your HashMap, try to override properly equals Method in the PojoClass.

@Data
@EqualsAndHashCode
class PojoClass {
private String name;
// getter + setter + equals +hashcode : generated by Lombok
}

public class Bounder {

static boolean pojoCounter(Map<String, Optional<PojoClass>> pojoMappedByName, PojoClass pojo) {
    long x = pojoMappedByName.values().stream().
            filter(e -> e.get().equals(pojo)).count();
    if (x == 0) {
        return false;
    } else
        return true;
}

public static void main(String[] args) throws IOException {
    PojoClass NotAPojo = new PojoClass();
    NotAPojo.setName("NotAPojo");

    PojoClass pojotest = new PojoClass();
    pojotest.setName("Tommy");
    PojoClass pojotest1 = new PojoClass();
    pojotest1.setName("Hammy");
    PojoClass pojotest2 = new PojoClass();
    pojotest2.setName("Mammy");
    PojoClass pojotest3 = new PojoClass();
    pojotest3.setName("Kammy");

    Map<String, Optional<PojoClass>> pojoMappedByName = new HashMap<String, Optional<PojoClass>>();

    pojoMappedByName.put("0", Optional.of(pojotest));
    pojoMappedByName.put("1", Optional.of(pojotest1));
    pojoMappedByName.put("2", Optional.of(pojotest2));
    pojoMappedByName.put("3", Optional.of(pojotest3));

    System.out.println("pojoMappedByName , pojotest :  exists ?  " + pojoCounter(pojoMappedByName, pojotest));
    System.out.println("pojoMappedByName , pojotest1   exists ?  " + pojoCounter(pojoMappedByName, pojotest1));
    System.out.println("pojoMappedByName , pojotest2   exists ?  " + pojoCounter(pojoMappedByName, pojotest2));
    System.out.println("pojoMappedByName , pojotest3   exists ?  " + pojoCounter(pojoMappedByName, pojotest3));
    System.out.println("pojoMappedByName , NotAPojo    exists ?  " + pojoCounter(pojoMappedByName, NotAPojo));

}
}

Output :

pojoMappedByName , pojotest : exists ? true

pojoMappedByName , pojotest1 exists ? true

pojoMappedByName , pojotest2 exists ? true

pojoMappedByName , pojotest3 exists ? true

pojoMappedByName , NotAPojo exists ? false

Comments

-1

You can override hashCode() and equals() methods and let equals function in PojoClass something to unique to make your comparison like id

@Override
public boolean equals(Object o) {
    if (o == this)
        return true;
    if (!(o instanceof PojoClass))
        return false;
    PojoClass other = (PojoClass)o;
    return this.id == other.id;
}

1 Comment

You've misunderstood the question. It's not about the PojoClass part. It's about the Optional part.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.