Related
I want to group different objects types based on same content of fields incomeCode, endDate and codeRef on both classes. I omitted many fields on both classes that make each object unique for simplicity.
public class Exon {
private Long id;
private IncomeCode incomeCode;
private LocalDate endDate;
String codeRef;
}
public class Sup {
private Long id;
private IncomeCode incomeCode;
private LocalDate startDate;
private LocalDate endDate;
String codeRef;
}
Exons example:
id
incomdeCode
endDate
codeRef
1
45
01/01/2021
4
2
21
01/01/2022
5
3
33
01/01/2023
2
4
45
01/01/2021
4
Sups example:
id
incomdeCode
endDate
codeRef
1
45
01/01/2021
4
2
21
01/01/2022
5
3
33
01/01/2023
2
Desired result :
List : { {exon1, exon4, sup1}, {exon2, sup2}, {exon3, sup3} }
My attempt :
public Map<Object, List<Exon>> getExons() {
Map<Object, List<Exon>> result = getSource1.stream()
.flatMap(lp -> lp.getExons().stream())
.collect(Collectors.groupingBy(e -> new KeyGroup(e.getIncomeCode(), e.getEndDate(), e.getCodeRef())
));
return result;
}
public Map<Object, List<Sup>> getSups() {
Map<Object, List<Sup>> result = getSource2.stream()
.flatMap(lp -> lp.getSups().stream())
.collect(Collectors.groupingBy(e -> new
KeyGroup(e.getIncomeCode(), e.getEndDate(), e.getCodeRef())));
return result;
}
Map<Object, List<Exon>> exonList = getExons();
Map<Object, List<Sup>> supList = getSups();
Map<Object, List<List<?>>> objMap = new HashMap<>();
exonList.forEach((k, v) -> {
if (objMap.containsKey(o)) {
objMap.get(o).add(v);
} else {
List<List<?>> eList = new ArrayList<>();
eList.add(v);
objMap.put(o, eList);
}
});
supList.forEach((o, v) -> {
if (objMap.containsKey(o)) {
objMap.get(o).add(v);
} else {
List<List<?>> eList = new ArrayList<>();
eList.add(v);
objMap.put(o, eList);
}
});
As it has been pointed out in the comments, if you need to mix different type of objects your resulting List will be a List<List<Object>>.
To group them by like that, you could use the collect() terminal operation in conjunction with a Collectors.groupingBy() which could group the objects with a key built ad-hoc with the desired fields (incomeCode, endDate and codeRef). After building the Map, you could retrieve its values, i.e. a Collection with the lists of objects, and give them in input to the Conversion Constructor of a List implementation.
List<List<Object>> listRes = new ArrayList<>(Stream.concat(listExon.stream(), listSup.stream())
.collect(Collectors.groupingBy(obj -> {
if (obj instanceof Exon) {
Exon exon = (Exon) obj;
return String.format("%s-%s-%s", exon.getIncomeCode(), exon.getEndDate(), exon.getCodeRef());
}
Sup sup = (Sup) obj;
return String.format("%s-%s-%s", sup.getIncomeCode(), sup.getEndDate(), sup.getCodeRef());
})).values());
Here there is also a link to test the code above:
https://ideone.com/OSTItQ
How about something like this. You can make another subclass and group your different class item on this map
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Solution
{
public void sol()
{
List<Exon> exons = new ArrayList<>();
List<Sup> sups = new ArrayList<>();
List<ClassWithNeededFields> list1 = exons.stream()
.map(item -> new ClassWithNeededFields(item.getIncomeCode(), "neededField"))
.collect(Collectors.toList());
List<ClassWithNeededFields> list2 = sups.stream()
.map(item -> new ClassWithNeededFields(item.getIncomeCode(), "neededField"))
.collect(Collectors.toList());
list1.addAll(list2);
Map<IncomeCode, List<ClassWithNeededFields>> map2 = list1.stream()
.map(item -> new ClassWithNeededFields(item.getIncomeCode(), "neededField"))
.collect(Collectors.groupingBy(ClassWithNeededFields::getIncomeCode));
}
public class Exon
{
private IncomeCode incomeCode;
public IncomeCode getIncomeCode()
{
return null;
}
}
public class Sup
{
private IncomeCode incomeCode;
public IncomeCode getIncomeCode()
{
return null;
}
}
public class IncomeCode
{
}
public class ClassWithNeededFields
{
private IncomeCode incomeCode;
private String otherField;
// The other needed fields. ...
public ClassWithNeededFields(IncomeCode incomeCode, String otherField /* The other needed fields. ... */)
{
}
public IncomeCode getIncomeCode()
{
return this.incomeCode;
}
}
}
This is a beginner's question to kafka-streaming.
How would you collect pairs of messages using the java kafka-streaming library and write them to a new output topic?
I was thinking about something like this:
private void accumulateTwo(KStream<String, String> messages) {
Optional<String> accumulator = Optional.empty();
messages.mapValues(value -> {
if (accumulator.isPresent()) {
String tmp = accumulator.get();
accumulator = Optional.empty();
return Optional.of(new Tuple<>(tmp, value));
}
else {
accumulator = Optional.of(value);
return Optional.empty();
}
}).filter((key, value) -> value.isPresent()).to("pairs");
Yet this will not work, since variables in Java Lambda expressions must be final.
Any ideas?
EDIT:
As suggested in the comments, three additional steps are necessary:
The Transformer must explicitly store its state within a state store. It will get a reference to the state store from the ProcessorContext, which it is getting passed in the init method.
The state store must be registered with the StreamsBuilder
The name of the state store must be passed within the transform method.
In this example it is sufficient to store the last message we have seen. We are using a KeyValueStore for this which will have exactly zero or one entry at each point in time.
public class PairTransformerSupplier<K,V> implements TransformerSupplier<K, V, KeyValue<K, Pair<V,V>>> {
private String storeName;
public PairTransformerSupplier(String storeName) {
this.storeName = storeName;
}
#Override
public Transformer<K, V, KeyValue<K, Pair<V, V>>> get() {
return new PairTransformer<>(storeName);
}
}
public class PairTransformer<K,V> implements Transformer<K, V, KeyValue<K, Pair<V, V>>> {
private ProcessorContext context;
private String storeName;
private KeyValueStore<Integer, V> stateStore;
public PairTransformer(String storeName) {
this.storeName = storeName;
}
#Override
public void init(ProcessorContext context) {
this.context = context;
stateStore = (KeyValueStore<Integer, V>) context.getStateStore(storeName);
}
#Override
public KeyValue<K, Pair<V, V>> transform(K key, V value) {
// 1. Update the store to remember the last message seen.
if (stateStore.get(1) == null) {
stateStore.put(1, value); return null;
}
KeyValue<K, Pair<V,V>> result = KeyValue.pair(key, new Pair<>(stateStore.get(1), value));
stateStore.put(1, null);
return result;
}
#Override
public void close() { }
}
public KStream<String, String> sampleStream(StreamsBuilder builder) {
KStream<String, String> messages = builder.stream(inputTopic, Consumed.with(Serdes.String(), Serdes.String()));
// 2. Create the state store and register it with the streams builder.
KeyValueBytesStoreSupplier store = Stores.persistentKeyValueStore(stateStoreName);
StoreBuilder storeBuilder = new KeyValueStoreBuilder<>(
store,
new Serdes.IntegerSerde(),
new Serdes.StringSerde(),
Time.SYSTEM
);
builder.addStateStore(storeBuilder);
transformToPairs(messages);
return messages;
}
private void transformToPairs(KStream<String, String> messages) {
// 3. reference the name of the state store when calling transform(...)
KStream<String, Pair<String, String>> pairs = messages.transform(
new PairTransformerSupplier<>(),
stateStoreName
);
KStream<String, Pair<String, String>> filtered = pairs.filter((key, value) -> value != null);
KStream<String, String> serialized = filtered.mapValues(Pair::toString);
serialized.to(outputTopic);
}
Changes to the state store can be watched using the console consumer:
./bin/kafka-console-consumer --topic <changelog-topic-name> --bootstrap-server localhost:9092
Full source code here: https://github.com/1123/spring-kafka-stream-with-state-store
Original Answer:
The JavaDoc of the org.apache.kafka.streams.kstream.ValueMapper interface states that it is for stateless record-by-record transformations, and that the org.apache.kafka.streams.kstream.Transformer interface, on the other hand, is
for stateful mapping of an input record to zero, one, or multiple new output records.
Therefore I guess the Transformer interface is the appropriate choice for collecting pairs of messages. This may only be of relevance in case of failure and restart of streaming applications, such that they can recover the state from Kafka.
Hence, here is another solution based upon the org.apache.kafka.streams.kstream.Transformer interface:
class PairTransformerSupplier<K,V> implements TransformerSupplier<K, V, KeyValue<K, Pair<V,V>>> {
#Override
public Transformer<K, V, KeyValue<K, Pair<V, V>>> get() {
return new PairTransformer<>();
}
}
public class PairTransformer<K,V> implements Transformer<K, V, KeyValue<K, Pair<V, V>>> {
private V left;
#Override
public void init(ProcessorContext context) {
left = null;
}
#Override
public KeyValue<K, Pair<V, V>> transform(K key, V value) {
if (left == null) { left = value; return null; }
KeyValue<K, Pair<V,V>> result = KeyValue.pair(key, new Pair<>(left, value));
left = null;
return result;
}
#Override
public KeyValue<K, Pair<V, V>> punctuate(long timestamp) {
return null;
}
public void close() { }
}
The PairTransformerSupplier is then used as follows:
private void accumulateTwo(KStream<String, String> messages) {
messages.transform(new PairTransformerSupplier<>())
.filter((key, value) -> value != null)
.mapValues(Pair::toString)
.to("pairs");
}
Trying out both solutions within a single process on a topic with a single partition yields, however, the exact same results. I have not tried with a topic with multiple partitions and multiple stream consumers.
You should be able to write an accumulator class
class Accumulator implements ValueMapper<String, Optional<Tuple<String>>> {
private String key;
public Optional<Tuple<String>> get(String item) {
if (key == null) {
key = item;
return Optional.empty();
}
Optional<Tuple<String>> result = Optional.of(new Tuple<>(key, item));
key = null;
return result;
}
}
and then process with
messages.mapValues(new Accumulator())
.filter(Optional::isPresent) // I don't think your filter is correct
.to("pairs");
I have an XML source from which I unmarshall Objects with JAXB.
The XML source:
<album>
<name>something</name>
<id>003030</id>
<artist>someone</artist>
...
</album>
The java source is like (with the required getter/setters as well):
#XmlRootElement(name="album")
class Album {
String name;
Long id;
String artist;
...
}
So far so good. Now I get some image urls in different sizes within album list:
...
<image size="small">http://.../small.jpg</image>
<image size="medium">http://.../medium.jpg</image>
<image size="large">http://.../large.jpg</image>
...
I want to map it to a java Map something like this:
Map<String,String> imageUrls;
Where the map's key would be the size attribute and the map's value would be the element value.
If it's possible, how should I annotate this variable?
helper class Pair
#XmlAccessorType(XmlAccessType.FIELD)
public class Pair {
#XmlAttribute
private String key;
#XmlValue
private String value;
public Pair() {
}
public Pair(String key, String value) {
this.key = key;
this.value = value;
}
//... getters, setters
}
List of pairs
#XmlAccessorType(XmlAccessType.FIELD)
public class PairList
{
private List<Pair> values = new ArrayList<Pair>();
public PairList() {
}
//...
}
adaptor
public class MapAdaptor extends XmlAdapter<PairList, Map<String, String>>
{
#Override
public Map<String, String> unmarshal(PairList list) throws Exception
{
Map<String, String> retVal = new HashMap<String, String>();
for (Pair keyValue : list.getValues())
{
retVal.put(keyValue.getKey(), keyValue.getValue());
}
return retVal;
}
#Override
public PairList marshal(Map<String, String> map) throws Exception
{
PairList retVal = new PairList();
for (String key : map.keySet())
{
retVal.getValues().add(new Pair(key, map.get(key)));
}
return retVal;
}
}
usage in your entity
#XmlJavaTypeAdapter(value = MapAdaptor.class)
private Map<String, String> imageUrls = new HashMap<String, String>();
PS
You can do it without class PairList using Pair[] instead of PairList
adaptor
public class MapAdaptor extends XmlAdapter<Pair[], Map<String, String>>
{
#Override
public Map<String, String> unmarshal(Pair[] list) throws Exception
{
Map<String, String> retVal = new HashMap<String, String>();
for (Pair keyValue : Arrays.asList(list))
{
retVal.put(keyValue.getKey(), keyValue.getValue());
}
return retVal;
}
#Override
public Pair[] marshal(Map<String, String> map) throws Exception
{
List<Pair> retVal = new ArrayList<Pair>();
for (String key : map.keySet())
{
retVal.add(new Pair(key, map.get(key)));
}
return retVal.toArray(new Pair[]{});
}
}
but in this case you can't control name of every pair. It will be item and you can't change it
<item key="key2">valu2</item>
<item key="key1">valu1</item>
PS2
If you will try use List<Pair> instead of PairList, you will get Exception
ERROR: java.util.List haven't no-arg constructor
I want to use MultiKeyMap from Apache Collection, because I need a HashMap with two keys and a value.
To put elements I do this:
private MultiKeyMap multiKey = new MultiKeyMap();
multiKey.put("key1.1", "key2.1", "value1");
And for get element I do this:
String s = multiKey.get("key1.1");
But the String s cames null... If I pass the two keys, like that:
String s = multiKey.get("key1.1", "key2.1");
The String s cames with values value1...
How can I extend the MultiKeyMap to get the right value when I pass only one of the two keys?
If you need only one key to get a value you have a plain old HashMap.
private Map<String, String> map = new HashMap<>();
map.put("key1.1", "value1");
map.put("key2.1", "value1");
And for get element you can do this:
String s = map.get("key1.1"); // s == "value1"
MultiKeyMap is required when both keys must be provided.
If you specify a value with two keys, you are going to need both keys to get it back. The hash function is not designed to return all the possible values that are associated with only one of the two keys. You may need to find a different data structure to do this.
MultiKeyMap is about using tuples as keys, not about matching one value to more than one key. Use a normal map and just put your value twice, with different keys.
Some more caution is needed when removing values. When you remove a value for the first key, do you want to automatically remove other keys with the same value? If so, you need either to loop over all keys and remove those with same value by hand, which could be inefficient, or keep some kind of reverse map to quickly find keys for specific value.
I don't know exact solution to your problem. But I suggest you to implement it like:
Map<K2, K1> m2;
Map<K1, V> m1;
And see: How to implement a Map with multiple keys?
It seems that you just do not need MultiKeyMap. You need regular map. Using it you can associate the same value with as many keys as you want.
Map<String, String> map = new HashMap<String, String>();
Object value = .....
map.put("key1", value);
map.put("key2", value);
..................
if(map.get("key1") == map.get("key2")) {
System.out.println("the same value stored under 2 different keys!");
}
You just can't since it's not the way a MultiKeyMap works. Put the value with separate keys and than try getting it with each key at a time.
Instead of that you can use table data stature from guava.
I would suggest to create a separate class for multiple keys:
public class Test {
Map<Shape, Book> test1 = new HashMap<>();
Book book = new Book("A");
test1.put(Shape, book);
private class Shape {
String id1;
String id2;
public Shape(String id1, String id2) {
this.id1 = id1;
this.id2 = id2;
}
#Override
public boolean equals(Object o) {//}
#Override
public int hashCode() {//}
}
}
Here is a simple MultiKeyMap implementation that worked for me.
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class MultiMap<K, V> implements Map<K, V>
{
private class MultiMapEntery implements java.util.Map.Entry<K, V>
{
private final K key;
private V value;
public MultiMapEntery(K key, V value)
{
this.key = key;
this.value = value;
}
#Override
public K getKey()
{
return key;
}
#Override
public V getValue()
{
return value;
}
#Override
public V setValue(V value)
{
V oldValue = this.value;
this.value = value;
return oldValue;
}
};
private final Map<K, String> keyMap = new HashMap<K, String>();
private final Map<String, Set<K>> inverseKeyMap = new HashMap<String, Set<K>>();
private final Map<String, V> valueMap = new HashMap<String, V>();
#Override
public void clear()
{
keyMap.clear();
inverseKeyMap.clear();
valueMap.clear();
}
#Override
public boolean containsKey(Object key)
{
return keyMap.containsKey(key);
}
#Override
public boolean containsValue(Object value)
{
return valueMap.containsValue(value);
}
#Override
public Set<java.util.Map.Entry<K, V>> entrySet()
{
Set<java.util.Map.Entry<K, V>> entries = new HashSet<>();
for(K key : keyMap.keySet())
{
V value = valueMap.get(key);
entries.add(new MultiMapEntery(key, value));
}
return entries;
}
#Override
public V get(Object key)
{
return valueMap.get(keyMap.get(key));
}
#Override
public boolean isEmpty()
{
return valueMap.isEmpty();
}
#Override
public Set<K> keySet()
{
return keyMap.keySet();
}
#Override
public V put(K key, V value)
{
String id = keyMap.get(key);
if(id == null)
{
id = UUID.randomUUID().toString();
}
keyMap.put(key, id);
Set<K> keys = inverseKeyMap.get(id);
if(keys == null)
{
keys = new HashSet<>();
}
keys.add(key);
inverseKeyMap.put(id, keys);
valueMap.put(id, value);
return value;
}
public V put(Set<K> keys, V value)
{
String id = null;
for(K key : keys)
{
id = keyMap.get(key);
if(id != null) // one of the keys already exists
{
break;
}
}
if(id == null)
{
id = UUID.randomUUID().toString();
}
for(K key : keys)
{
keyMap.put(key, id);
}
inverseKeyMap.put(id, keys);
valueMap.put(id, value);
return value;
}
#Override
public void putAll(Map<? extends K, ? extends V> map)
{
for(java.util.Map.Entry<? extends K, ? extends V> entry : map.entrySet())
{
put(entry.getKey(), entry.getValue());
}
}
#Override
public V remove(Object key)
{
String id = keyMap.get(key);
keyMap.remove(key);
Set<K> keys = inverseKeyMap.get(id);
keys.remove(key);
V value = valueMap.get(id);
if(keys.size() == 0) // it was the last key, now remove the value
{
valueMap.remove(id);
}
return value;
}
#Override
public int size()
{
return valueMap.size();
}
#Override
public Collection<V> values()
{
return valueMap.values();
}
public static void main(String[] args)
{
MultiMap<String, String> m = new MultiMap<>();
m.put("a", "v1");
Set<String> s = new HashSet<>();
s.add("b");
s.add("c");
s.add("d");
m.put(s, "v2");
System.out.println("size:" + m.size());
System.out.println("keys:" + m.keySet());
System.out.println("values:" + m.values().toString());
System.out.println("a:" + m.get("a"));
System.out.println("b:" + m.get("b"));
System.out.println("c:" + m.get("c"));
System.out.println("d:" + m.get("d"));
m.remove("a");
System.out.println("size:" + m.size());
System.out.println("keys:" + m.keySet());
System.out.println("values:" + m.values().toString());
System.out.println("a:" + m.get("a"));
System.out.println("b:" + m.get("b"));
System.out.println("c:" + m.get("c"));
System.out.println("d:" + m.get("d"));
s.add("a");
m.put(s, "v3");
System.out.println("size:" + m.size());
System.out.println("keys:" + m.keySet());
System.out.println("values:" + m.values().toString());
System.out.println("a:" + m.get("a"));
System.out.println("b:" + m.get("b"));
System.out.println("c:" + m.get("c"));
System.out.println("d:" + m.get("d"));
}
}
A little late, but you probably mean to get every result from the map, that matches the first element only, even though it contains multiple results, ignoring the second key (wildcard effect). Apache's MultiKeyMap is not suitable for this.
You could solve this by creating your own filter functionality using the MultiKey of MultiKeyMap. First, filter out only the relevant MultiKeys (which you get from yourMultiKeyMap.keySet() ) . The following method takes those multiKeys, and the first keys you want to filter on:
private Set<MultiKey<? extends String>> filterMultiKeys(Set<MultiKey<? extends String>> multiKeys, final String... keys) {
final List<String> givenKeys = Arrays.asList(keys);
return multiKeys.stream().filter(multiKey -> {
final Object[] actualKeys = multiKey.getKeys();
if (actualKeys.length < givenKeys.size()) {
// Lesser keys, so never a match
return false;
}
final List<Object> trimmedKeys = Arrays.asList(actualKeys).subList(0, givenKeys.size());
return trimmedKeys.equals(givenKeys);
}).collect(Collectors.toSet());
}
Then, use the resulting MultiKeys to get the results:
final Set<String> results = filteredKeys.stream().map(multiKey -> yourMultiKeyMap.get(multiKey)).collect(Collectors.toSet());
For bonus points, one could extend or decorate MultiKeyMap and create MyMultiKeyMap , having a method like match(keys...) using the filter functionality.
I need a mapping from a list of keys to a value. I know I could write my own code like this:
Map<Person, Map<Daytime, Map<Food, Integer>>> eaten = ...;
Now I want to have some get and put methods like these:
Integer numberOfEggsIAteInTheMorning = eaten.get(me, morning, scrambledEggs);
eaten.put(me, evening, scrambledEggs, 1);
Do you know of an existing class that has this kind of API? I'm too lazy of writing it myself. ;)
If you look for a more generic approach, and you might have more than 2 or 3 'chain steps', I would suggest in applying some different structural approach, rather than sticking to using only basic collection classes. I have feeling that Composite Pattern could be the right choice if it's correctly applied.
EDIT: due to example requested
The full example would be somewhat time consuming, so let me just explain my idea with dirty Java/pseudocode mix (I'm not even sure if I've missed something!!!). Let's consider we have class BaseMap:
abstract class BaseMap {
public abstract Object getValue(Object.. keys);
public abstract void putValue(Object value, Object.. keys);
}
Then we could have ObjectMap that would be the 'leaf' of our composite structure:
class ObjectsMap extends BaseMap {
private Map<Object, Object> map = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length == 1
return map.get(keys[0]);
}
public void putValue(Object value, Object.. keys) {
// assert that keys.length = 1
map.put(keys[0], value);
}
}
And the actual composite would be as such:
class CompositeMap extends BaseMap {
private Map<Object, BaseMap> compositeMaps = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length > 1
return compositeMap.get(keys[0]).getValue(/* System.arrayCopy => subset of elements {keys_1, .. ,keys_max} */);
}
public void putValue(Object value, Object.. keys) {
// assert keys.length > 1
BaseMap newMap = null;
if (keys.length = 2) -> newMap = new ObjectsMap()
else newMap = new CompositeMap();
newMap.putValue(value, /*subset of keys {keys_1, .. , keys_max}*/);
}
}
You can use org.apache.commons.collections.keyvalue.MultiKey for that: Map<Multikey, Object>
It would be hard to implement a general chained map.
How would the declaration of the class look like? (You can't have a variable number of type parameters.
class ChainedMap<K1..., V>
Another option would be to have a ChainedMapUtil class that performs put / get recursively.
Here is an example of a recursive get. (Quite ugly solution though I must say.)
import java.util.*;
public class Test {
public static Object chainedGet(Map<?, ?> map, Object... keys) {
Object k = keys[0];
if (!map.containsKey(k)) return null;
if (keys.length == 1) return map.get(k);
Object[] tailKeys = Arrays.copyOfRange(keys, 1, keys.length);
return chainedGet((Map<?,?>) map.get(k), tailKeys);
}
public static void main(String[] arg) {
Map<String, String> m1 = new HashMap<String, String>();
m1.put("ipsum", "dolor");
Map<Integer, Map<String, String>> m2 =
new HashMap<Integer, Map<String, String>>();
m2.put(17, m1);
Map<String, Map<Integer, Map<String, String>>> chained =
new HashMap<String, Map<Integer, Map<String, String>>>();
chained.put("lorem", m2);
System.out.println(chainedGet(chained, "lorem", 17, "ipsum")); // dolor
System.out.println(chainedGet(chained, "lorem", 19, "ipsum")); // null
}
}
If you are going to write your own, I would suggest
eaten.increment(me, evening, scrambledEggs);
You could use a composite key
eaten.increment(Key.of(me, evening, scrambledEggs));
(TObjectIntHashMap supports increment and adjust)
You may not even need a custom key.
eaten.increment(me + "," + evening + "," + scrambledEggs);
It is fairly easy to decompose the key with split()
I once made a map using 3 keys just for fun.May be you can use it instead of using chained maps:
public class ThreeKeyMap<K1,K2,K3,V>{
class wrap{
K1 k1;
K2 k2;
K3 k3;
public wrap(K1 k1,K2 k2,K3 k3) {
this.k1=k1;this.k2=k2;this.k3=k3;
}
#Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
wrap o=(wrap)arg0;
if(!this.k1.equals(o.k1))
return false;
if(!this.k2.equals(o.k2))
return false;
if(!this.k2.equals(o.k2))
return false;
return true;
}
#Override
public int hashCode() {
int result=17;
result=37*result+k1.hashCode();
result=37*result+k2.hashCode();
result=37*result+k3.hashCode();
return result;
}
}
HashMap<wrap,V> map=new HashMap<wrap, V>();
public V put(K1 k1,K2 k2,K3 k3,V arg1) {
return map.put(new wrap(k1,k2,k3), arg1);
}
public V get(Object k1,Object k2,Object k3) {
return map.get(new wrap((K1)k1,(K2)k2,(K3)k3));
}
public static void main(String[] args) {
ThreeKeyMap<Integer,Integer,Integer,String> birthDay=new ThreeKeyMap<Integer, Integer, Integer, String>();
birthDay.put(1, 1,1986,"Emil");
birthDay.put(2,4,2009, "Ansih");
birthDay.put(1, 1,1986,"Praveen");
System.out.println(birthDay.get(1,1,1986));
}
}
UPDATE:
As #Arturs Licis suggested.I looked up in net for composite pattern and I wrote a sample using it.I guess this is composite..Please comment if it is not so.
Person class:
public class Person {
private final String name;
private Map<Time, Food> map = new HashMap<Time, Food>();
public Person(String name) {
this.name = name;
}
void addTimeFood(Time time, Food food) {
map.put(time, food);
}
public String getName() {
return name;
}
Food getFood(Time time) {
Food tmp = null;
return (tmp = map.get(time)) == null ? Food.NoFood : tmp;
}
// main to test the person class
public static void main(String[] args) {
Person p1 = new Person("Jack");
p1.addTimeFood(Time.morning, Food.Bread);
p1.addTimeFood(Time.evening, Food.Chicken);
Person p2 = new Person("Jill");
p2.addTimeFood(Time.morning, Food.Egg);
p2.addTimeFood(Time.evening, Food.Rice);
Map<String, Person> map = new HashMap<String, Person>();
map.put(p1.getName(), p1);
map.put(p2.getName(), p2);
System.out.println(map.get("Jack").getFood(Time.evening));
}
#Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(name).append("\n");
b.append(map);
return b.toString();
}
}
Food class:
public enum Food {
Rice,
Egg,
Chicken,
Bread,
NoFood;
}
Time class:
public enum Time {
morning,
evening,
night
}