Function .contains() not working in Groovy on expected way
Asked Answered
R

2

7

I am trying to check if number is member of list by using Groovy programming language.

I have this piece of code:

List<Long> list = [1, 2, 3]
Long number = 3

println(list.contains(number))​

Expected output is true, but the result I get is false.

Does anybody have an explanation?

Robers answered 30/4, 2018 at 14:31 Comment(2)
Have you tried a primitive long?Aubreyaubrie
I know how to handle this, but I am more interested why is this not working.Robers
O
3

Generic type parameters don't feature at runtime. Check this:

List<Long> list = [1, 2, 3]
list.each{println it.getClass()}

Which prints:

class java.lang.Integer
class java.lang.Integer
class java.lang.Integer

The true confusion is introduced by the bizarre behavior difference between .equals and == implementations:

Long.valueOf(3).equals(Integer.valueOf(3))
===> false
Long.valueOf(3) == Integer.valueOf(3)
===> true

List.contains seems to be using .equals, which checks the class of the parameter, thus explaining why forcing element types to Long resolves the problem.

So, in the midst of this uncertainty, I think the only sure thing is that Groovy's == execution performs the most intuitive and predictable comparison. So I'd change the check to:

boolean contains = list.grep{it == 3L} //sets value to true if matches at least 1

It helps when one doesn't have to be cognizant of data types linked to literals:

def ints = [1, 2, 3]
def longs = [1L, 2L, 3L]

boolean found1 = ints.grep{it == 3L}
boolean found2 = ints.grep{it == 3}
boolean found3 = longs.grep{it == 3L}
boolean found4 = longs.grep{it == 3}

println found1
println found2
println found3
println found4

Which works as anyone would want:

true
true
true
true
Overmatter answered 30/4, 2018 at 15:21 Comment(0)
W
1

You should use the special literal values for longs: 1L instead of 1.

List<Long> list = [1L, 2L, 3L]
Long number = 3L

1 is an int. 1L is a long.

Wills answered 30/4, 2018 at 14:36 Comment(3)
I get that, but for me is very strange that if I declare list of Long-s, that they won't be recognised as Long-sRobers
I agree it's unexpected (I'm coming from a Java background too.) Note that this will work as you expect: list.any(number)Poundfoolish
This is how generics are implemented in the JVM. You can use GroovyConsole or a debugger to inspect the actual list. The type is ArrayList. The information of the element type is lost. You can even write List<String> list = [1L, 2L, 3L] and it wouldn't matter.Wills

© 2022 - 2024 — McMap. All rights reserved.