Search code examples
javakotlindata-structurescollections

Choosing a datastructure for working with small dataset


I have a small set of Java POJOs, each them has several fields. I do not plan to add fields to them on the fly. Size of my set is small: it's not about some "bigdata", but rather about state of my application. I.e my data holder class looks like

class MyObject
{
    private int a
    private String b
    private boolean c
}

I need some datastructure which can help me to make queries to this data by some field values: by a=5, b="abc" or c=true. For now it's OK to query by one fields only, i.e. I don't need complex queries.

A straightforward solution is to use maps:

Map<Integer, MyObject> aMap = new HashMap<>()
Map<String, MyObject> bMap = new HashMap<>()
Map<Boolean, MyObject> cMap = new HashMap<>()

The questions are:

  1. Is there a way to make it better? Java version up to latest one.
  2. Is there a way to make it better in Kotlin?
  3. (optional requirement so far) What if I need to store it to make this collection survive application restart?

Solution

  • Instead of creating a separate data structure You can use a Specification Pattern to evaluate if an object satisfy a condition, using this pattern you can do any sort of combination and define even more complex rules

    Here is an example (Kotlin version) :

    import java.util.Objects
    
    interface Specification<T> {
        fun isSatisfiedBy(obj: T): Boolean
    }
    
    class Or<T>(private val spec1: Specification<T>, private val spec2: Specification<T>) : Specification<T> {
        override fun isSatisfiedBy(obj: T) = spec1.isSatisfiedBy(obj) || spec2.isSatisfiedBy(obj)
    }
    
    class And<T>(private val spec1: Specification<T>, private val spec2: Specification<T>) : Specification<T> {
        override fun isSatisfiedBy(obj: T) = spec1.isSatisfiedBy(obj) && spec2.isSatisfiedBy(obj)
    }
    
    class FieldValue<T, V>(private val property: (T)->V, private val value: V): Specification<T> { 
        override fun isSatisfiedBy(obj: T) = property(obj) == value 
    }
    

    and you can use it like this :

    fun main() {
        val myObjects: List<MyObject> = listOf(
        MyObject(1, "abc", true),
        MyObject(2, "def", false),
        MyObject(3, "abc", false),
        MyObject(4, "ghi", true),
        MyObject(5, "abc", true)
    )
        
        // check the following condition a=5 OR (b=abc AND c = true)
        val spec: Specification<MyObject> = Or(
            FieldValue(MyObject::a, 5),
            And(
                FieldValue(MyObject::b, "abc"),
                FieldValue(MyObject::c, true)
            )
        )
        
        val results = myObjects.filter(spec::isSatisfiedBy)
    }
    

    Now this will filter on the fly your list so you need only to maintain your initial list of MyObject