0

I have a collection of 10,000 objects that have two properties: a string and a string[]

class Product
{
  String name;
  String[] aliases;
}

Each object is roughly 64 bytes, so we're looking at under a megabyte in memory for the whole collection, which seems manageable for Android to search in memory.

I want an exact match to either the single string, or any string in the array, and to then return the name property of the object.

However, android has some restrictions on Lambdas and .stream() that exclude many of the popular search solutions without forcing users to have OS 7.0 or above, which rules out roughly one third of users. I'd like to avoid that.

Currently, the following code is working, but seems terrible considering some of options I've seen out there which take advantage of streams and lambdas.

public String isProductOrAlias(String lowertext) {
    //products is instantiated in the parent class 
    //as a List<Product> loaded into memory from a local file
    try {
        for (Product p: products) {
            if(lowertext.equals(p.name.toLowerCase()))
                return p.name;
            if(p.aliases != null) {
                for (String s: p.aliases) {
                    if(lowertext.equals(s.toLowerCase()))
                        return p.name;
                }
            }
        }
    } catch (Exception ex) {
        return "";
    }
    return "";
}

What's the best way to achieve the same goal, given the restrictions of the environment? Ideally, we'd like to keep minSDK to 23 (6.0) and above.

Wesley
  • 5,381
  • 9
  • 42
  • 65
  • 3
    What exactly is terrible about this code? Except for formatting and a catch-all try block. – Egor Sep 29 '18 at 00:19
  • Other than getting rid of the unnecessary try-catch, the only other thing I'd improve in that code is to use `equalsIgnoreCase` to get rid of those `toLowerCase` calls that generate unnecessary garbage. – Andreas Sep 29 '18 at 00:23
  • are you looking to do this using stream and lambda ? – Ryuzaki L Sep 29 '18 at 00:27
  • 2
    You tagged this with `kotlin` so ... why not do it in Kotlin? – dominicoder Sep 29 '18 at 01:24
  • 1
    `fun isProductOrAlias(lt: String) = products.find { it.name.toLowerCase() == lt || it.aliases.any { it.toLowerCase() == lt } }?.name ?: ""` should be kotlin and works on any api level – zapl Sep 29 '18 at 02:01

1 Answers1

1

Canonical Kotlin, which would work on any version of Android regardless of Java Streams since it doesn't use them would be:

data class Product(val name: String, val aliases: Array<String>)

// written as an extension function, but doesn't have to be...
fun List<Product>.isProductOrAlias(text: String): String {
   return this.firstOrNull { product -> 
       product.name.equals(text, ignoreCase = true) ||
       product.aliases.any { alias -> 
           alias.equals(text, ignoreCase = true)
       }
   }?.name ?: ""
}

fun test() {
   // assume somewhere this was created
   val products = listOf<Product>(...)

   println(products.isProductOrAlias("something")) // prints name of Product or ""
}

See Kotlin API reference for the standard library:

Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
  • Nice, I didn't realize Kotlin would have features Java didn't. Is this (in your opinion) the fastest possible solution? I've used Hash collections in the past for flat data structures, and just want to avoid iterating through every item if it isn't necessary. I don't know Kotlin well enough to know if this is doing that or not. – Wesley Oct 02 '18 at 21:48
  • @Wesley you asked this in another SO question, I answered there already: https://stackoverflow.com/questions/52563292/rapid-low-power-cpu-string-matching-in-android/52565631#52565631 – Jayson Minard Oct 02 '18 at 22:25