Project Euler #12 in Scala — Highly divisible triangular numbers
Your triangular number "cache" isn't an idiomatic technique in functional programming.  Since triangular numbers constitute an infinite sequence, it would be more elegant to model them in Scala as an infinite lazy stream, especially since you only require sequential access.  I would define it this way:

val triangleNumbers = Stream.from(1).scan(0)(_ + _)


Or, if you prefer to have the sequence start with 1 rather than 0:

val triangleNumbers = Stream.from(2).scan(1)(_ + _)




nFactors could be optimized a bit.


I would call the parameter n, which has the connotation of being an integer, instead of x, which has the connotation of being floating point.
The .floatValue casts are superfluous, and it is safe to round the square root down using .intValue rather than sometimes rounding up.
Personally, I would prefer the name middleFactor instead of maxFactor.
The lambda in .filter(f =&gt; x % f == 0) could be more idiomatically written as .filter(x % _ == 0).
You don't need to use sets.  Just a count of factors will do, along with a check of whether middleFactor is being double-counted.




def nFactors(n: Int): Int = {
  val middleFactor = math.sqrt(n).intValue
  val smallFactors = (1 to middleFactor).count(n % _ == 0)
  2 * smallFactors - (if (middleFactor * middleFactor == n) 1 else 0)
}




In main, the .dropWhile(…).next would be more clearly written as .find(…).

def main(args: Array[String]): Unit = {
  triangleNumbers.find(nFactors(_) &gt; 500).foreach(println)
}