Java Map with keys of 3 columns
Asked Answered
V

9

8

I need a map in which my key should be based on 3 columns, say C1, C2, C3. C1 has highest priority. C2 has one less than C1 and C3 has one less than C2.

How do i create key in the map such that if somebody asks for information on C1, I should able to give all the values which has C1. I also should be able to return all the values if asked for C1 & C2

Verbalize answered 21/3, 2013 at 6:2 Comment(5)
Can you show us some test cases? What is your input and your output?Hearse
Question is not clear....Cooking
This is not a standard problem. Lets approach this problem specific to current scenario for your usage. Can you please tell what is the data type of C1,C2 and C3 ? If they are integers then what would be the range of them?Grappa
Can you share some sample values for C1, C2 and C3 in a tabular format with columns C1, C2 and C3 ?Malda
I don't understand the question, but also see SortedMap with multiple fields for keys and values, and How to implement a Map with multiple keys?,Bandolier
G
2

You can use the same strategy as multicolumn indexes in databases, if your key columns can be ordered (i.e., in Java, they need to be Comparable) and can easily define maximum and minimum values for all but the first.

An example with integer columns:

public class Key implements Comparable<Key> {
    int c1, c2, c3;

    private static final int c2_min = Integer.MIN_VALUE;
    private static final int c2_max = Integer.MAX_VALUE;
    private static final int c3_min = Integer.MIN_VALUE;
    private static final int c3_max = Integer.MAX_VALUE;

    @Override
    public int compareTo(Key o) {
        if (c1!=o.c1) return Integer.compare(c1, o.c1);
        if (c2!=o.c2) return Integer.compare(c2, o.c2);
        return Integer.compare(c3, o.c3);
    }

    // constructor, equals, ...

}

and then you can get all entries for some value k1 in c1 like this:

map.subMap(new Key(k1, Key.c2_min, 0), new Key(k1, Key.c2_max, 0));

Likewise, using the first two columns:

map.subMap(new Key(k1, k2, Key.c3_min), new Key(k1, k2, Key.c3_max));
Gallantry answered 21/3, 2013 at 9:13 Comment(2)
Thanks, I have question my type is byte[]. How Do I define MAX and MIN ValueVerbalize
Assuming a byte comparator, you can use MIN = new byte[0]. For MAX, if you have a maximum array size, just use it and initialize all elements with Byte.MAX_VALUE. Otherwise, you have to choose some instance (defined as static final) and explicitly check for it in compareTo with ==. Finally, depending on your application, null might also work as a maximum value by considering it explicitly in compareTo.Gallantry
U
2

Use three maps.

One Map<C1, V> and one Map<C2, V> and one Map<C3, V>. 

You can wrap the three maps into a class and implement you method.

Unlearned answered 21/3, 2013 at 6:5 Comment(0)
G
2

You can use the same strategy as multicolumn indexes in databases, if your key columns can be ordered (i.e., in Java, they need to be Comparable) and can easily define maximum and minimum values for all but the first.

An example with integer columns:

public class Key implements Comparable<Key> {
    int c1, c2, c3;

    private static final int c2_min = Integer.MIN_VALUE;
    private static final int c2_max = Integer.MAX_VALUE;
    private static final int c3_min = Integer.MIN_VALUE;
    private static final int c3_max = Integer.MAX_VALUE;

    @Override
    public int compareTo(Key o) {
        if (c1!=o.c1) return Integer.compare(c1, o.c1);
        if (c2!=o.c2) return Integer.compare(c2, o.c2);
        return Integer.compare(c3, o.c3);
    }

    // constructor, equals, ...

}

and then you can get all entries for some value k1 in c1 like this:

map.subMap(new Key(k1, Key.c2_min, 0), new Key(k1, Key.c2_max, 0));

Likewise, using the first two columns:

map.subMap(new Key(k1, k2, Key.c3_min), new Key(k1, k2, Key.c3_max));
Gallantry answered 21/3, 2013 at 9:13 Comment(2)
Thanks, I have question my type is byte[]. How Do I define MAX and MIN ValueVerbalize
Assuming a byte comparator, you can use MIN = new byte[0]. For MAX, if you have a maximum array size, just use it and initialize all elements with Byte.MAX_VALUE. Otherwise, you have to choose some instance (defined as static final) and explicitly check for it in compareTo with ==. Finally, depending on your application, null might also work as a maximum value by considering it explicitly in compareTo.Gallantry
A
0

By "priority", I presume you mean what is usually referred to as primary, secondary and tertiary keys.

If they're all string fields, concatenate them into a single string and use that as the key. In your case, the key is C1+C2+C3 (where "+" refers to string concatenation).

Acquiescent answered 21/3, 2013 at 6:5 Comment(1)
but then how will you fetch values only corresponding to C1 or only C1 and C2?Grappa
O
0

A Map will always return just one value for a key. You can't have it return multiple values based on your key classes content.

The simple way is to keep a separate map for each key type and return the appropriate results based on the passed key.

Odont answered 21/3, 2013 at 6:8 Comment(0)
C
0

A three-level index where a higher-level key can be used to access all lower level keys and objects will require a three-level map.

class ThreeLevelMap<K1,K2,K3,V>
{
    private Map<K1,Map<K2,Map<K3,V>>> store = new HashMap<K,Map<K2,Map<K3,V>>>();
    ...
    public V put(K1 key1, K2 key2, K3 key3, V value) { ... }
    public V get(K1 key1, K2 key2, K3 key3) { ... }

    public static class TLMEntry<K1,K2,K3,V>
    {
    ...
    }
    public Collection<TLMEntry<K1,K2,K3,V>> get(K1 key1, K2 key2) { ... }
    public Collection<TLMEntry<K1,K2,K3,V>> get(K1 key1) { ... }
}

This is a basic skeleton but should get you going in the right direction.

Collimate answered 21/3, 2013 at 6:24 Comment(0)
F
0

This seems more like a database problem. If you had a database with a table structured like:

CREATE TABLE MyMap (
    id IDENTITY PRIMARY KEY,
    c1 int, -- Change data types as needed.
    c2 int,
    c3 int,
    v  int);

then you would simply issue SELECT statements against it. You might want to use any of the in-memory Java databases.

If you don't want to do that, you could do the equivalent purely in Java functionally by writing a container class value class:

class Cdata {
    private int c1;
    private int c2;
    private int c3;
    private int v;
    // Constructors and getters elided.
    public boolean match(int c1) {
        return this.c1 == c1;
    }
    public boolean match(int c1, int c2) {
        return match(c1) && this.c2 == c2;
    }
    public boolean match(int c1, int c2, int c3) {
        return match(c1, c2) && this.c3 == c3;
    }
}

Then create a List and use a functional programming library with filter methods. Or, wait for Java 8 lambdas. Using a Map<Integer, Map<Integer, Map<Integer, Integer>>>> is too confusing.

Farris answered 21/3, 2013 at 6:34 Comment(0)
B
0

You can use TreeMap to acheive your usecase. I am assuming the following: Your three columns map to 3 increasing integer values i.e

C1 = 1, C2 = 2, C3 =3

where C1 = 1 is the highest priority and C2 = 2 is next in line and so on.

Note: Your keys need not always be Integers, you can use any type if you provide the proper Comparator to your TreeMap.

With this in place, you could do something like:

TreeMap<Integer, String> treeMap = new TreeMap<Integer, String>();
treeMap.put(1, "One");
treeMap.put(2, "two");
treeMap.put(3, "three");

List<String> list = getMappedValues(treeMap, 1);// returns One, Two, Three
//List<String> list = getMappedValues(treeMap, 2);// returns Two, Three
//List<String> list = getMappedValues(treeMap, 3);// returns Three
//List<String> list = getMappedValues(treeMap, 4);// returns null
if(list != null){
    //do something with the list of values
}

private static List<String> getMappedValues(TreeMap<Integer, String> map, Integer key) {
    Entry<Integer, String> e = map.ceilingEntry(key);
    if(e == null){
        return null;
    }
    List<String> list = new ArrayList<String>();
    while(e != null){
        list.add(e.getValue());
        key = e.getKey();
        e = map.higherEntry(key);
    }
    return list;
}
Backwardation answered 21/3, 2013 at 7:55 Comment(0)
C
0

private class C {

    public C() {
        Map <Object ,String> ObjectC =new HashMap<Object, String>();
    }
}

private class B {

    public B() {
        Map <Object ,C> ObjectB =new HashMap<Object, C>();
    }
}

private class A {

    public A() {
        Map <Object ,B> ObjectA =new HashMap<Object, B>();
    }
}

So this could promise you a beautiful structure for which you don't need do more work further as You can get All the content of A

MapVar.ObjectA.get(C1);

if both C1, C2 are given You can call

MapVar.ObjectA.get(C1).ObjectB.get(C2);

if both C1, C2 , C3 are given You can call

MapVar.ObjectA.get(C1).ObjectB.get(C2).ObjectC.get(C3);;

Then you can simply iterate through the map and get values .

Cotswold answered 29/3, 2013 at 8:54 Comment(0)
B
0

Taking inspiration from how we would've handled this if we had access to a database.

Suppose, we have a table with columns as - C1, C2, C3, Value.

And given the requirement that following would be the query patterns:

  1. select * from tbl where C1 = "input1";
  2. select * from tbl where C1 = "input1" and C2 = "input2";
  3. select * from tbl where C1 = "input1" and C2 = "input2" and C3 = "input3";

In such scenarios, it would be better if we use a multi-column index on (C1, C2, C3) in that particular order - which would internally create 3 separate indexes as:

  • Index1 - C1 (Useful for queries of type 1.)
  • Index2 - C1 and C2 (Useful for queries of type 2.)
  • Index3 - C1 and C2 and C3 (Useful for queries of type 3.)

We can follow a similar strategy in Java - to define 3 maps:

  • Map1 - Map<C1, Set>
  • Map2 - Map<C1+C2, Set> (Here key is concatenation of string values of C1 and C2)
  • Map3 - Map<C1+C2+C3, Set> (Here key is concatenation of string values of C1, C2 and C3)

So, based on the keys passed for fetch query, we pick the appropriate map. For example:

  • get(K1) - we will fetch from Map1
  • get(K1, K2) - we will fetch from Map2
  • get(K1, K2, K3) - we will fetch from Map3

Note that, this would require us to insert/delete in/from all the 3 maps for registering new data and deleting existing data

Butyrate answered 1/2, 2023 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.