Keying a collection by type - java

I'm looking to have a collection of objects that implement a certain interface, but I'd like to only have one per concrete type within the collection.
collection of implementers of dog:
- instance of dachshund
- instance of beagle
- instance of corgi
In .NET, there's a "KeyedByTypeCollection". Does something similar exist in Java in such a way that I could use it on Android?
Thanks!

If you're willing to use third-party libraries -- and if you don't care about maintaining order -- Guava's ClassToInstanceMap seems applicable here.
ClassToInstanceMap<Dog> map = MutableClassToInstanceMap.create();
map.putInstance(Corgi.class, new Corgi("Spot"));
map.putInstance(Beagle.class, new Beagle("Lady"));
Corgi corgi = map.getInstance(Corgi.class); // no cast required
(Disclosure: I contribute to Guava.)

You should look at generics. E.g.:
List<Dogs> dogList = new ArrayList<Dogs>();
EDIT: to have only unique instances in your collection, you should use Set<Dogs> dogList = new HashSet<Dogs>();

this might be what you are looking for:
see the comments in codes
// two Dog(interface) implementations
// Beagle, Dachshund implements Interface Dog.
final Dog d1 = new Beagle();
final Dog d2 = new Dachshund();
// here is your collection with type <Dog>
final Set<Dog> set = new HashSet<Dog>();
set.add(d1);
set.add(d2);
// see output here
for (final Dog d : set) {
System.out.println(d.getClass());
}
// you can fill them into a map
final Map<Class, Dog> dogMap = new HashMap<Class, Dog>();
for (final Dog d : set) {
// dog instances with same class would be overwritten, so that only one instance per type(class)
dogMap.put(d.getClass(), d);
}
the output of system.out.println line would be something like:
class test.Beagle
class test.Dachshund

I think you need a custom HaspMap that will maintain multiple values with same key,
So, create an simple class that extends HashMap and put values into it.
public class MyHashMap extends LinkedHashMap<String, List<String>> {
public void put(String key, String value) {
List<String> current = get(key);
if (current == null) {
current = new ArrayList<String>();
super.put(key, current);
}
current.add(value);
}
}
Now, create the instance of MyHashMap and put values into it as below,
MyHashMap hashMap = new MyHashMap();
hashMap.put("dog", "dachshund");
hashMap.put("dog", "beagle");
hashMap.put("dog", "corgi");
Log.d("output", String.valueOf(hashMap));
OUTPUT
{dog=[dachshund, beagle, corgi]}

Related

How can I make a new HashMap that won't change the one I've copied from?

I have a HashMap called the Item Database, which stores data about all of the items.
However, these items can have modifiers (in this case, the stat multiplier is important). Whenever I change an item to just one specific item drop, it ends up changing the base item from the HashMap.
For example, whenever a player creates a Katana, it does something like this.
HashMap<String, CustomItem> db = new HashMap<String, CustomItem>();
db.putAll(ItemDatabase.database);
CustomItem ci = db.get("KATANA");
From there, modifiers are applied via a getBukkitItem function on the CustomItem ci, basically multiplying a lot of the stats on that CustomItem and applying it.
baseHealth = (int) ((abbaseHealth / 100.0) * multiplier);
and other stats like that.
However, whenever I make changes to this new CustomItem, it also applies to the ItemDatabase hashmap. This means that whenever somebody makes another Katana, those multiplied stats become the new base stats to be multiplied.
TL;DR Whenever I'm changing a variable I got from a HashMap (db), that change also applies to the HashMap (itemdb). This happens even if the HashMap (db) it's from, is a copy of another HashMap (itemdb)
I have tried the method above, and using .clone() on a HashMap and casting it back to HashMap. Unfortunately I'm not really sure what else to try.
you should create a new object of deep clone. Using orika framework like below.
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
#Test
public void t() {
Map<Integer, User> map = new HashMap<>();
User one = new User();
one.setName("one");
System.out.println(one);
User two = new User();
two.setName("two");
System.out.println(two);
map.put(1,one);
map.put(2,two);
TypeBuilder<Map<Integer,User>> typeBuilder = new TypeBuilder<Map<Integer,User>>() {
};
Type<Map<Integer,User>> type = typeBuilder.build();
Map<Integer,User> copyMap = mapperFactory.getMapperFacade().mapAsMap(map, type,type);
System.out.println(copyMap.get(1));
System.out.println(copyMap.get(2));
}
You need to make new CustomItem instances. If you only make a copy of the Map, you’re just copying the references stored in the Map; they’ll still refer to the same CustomItem instances.
You can make this easier by adding a copy constructor or clone() method to CustomItem. Example of a copy constructor:
public class CustomItem {
public CustomItem(CustomItem other) {
this.name = other.name;
this.baseHealth = other.baseHealth;
this.multiplier = other.multiplier;
// Don't want two instances to refer to the same List!
this.inventoryList = new ArrayList<>(other.inventoryList);
// etc.
}
}
Example of a clone() method:
public class CustomItem
implements Cloneable {
#Override
public CustomItem clone()() {
try {
CustomItem copy = (CustomItem) super.clone();
// Don't want two instances to refer to the same List!
copy.inventoryList = new ArrayList<>(copy.inventoryList);
// etc.
return copy;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
Once you have a way to copy CustomItem instances, you need to use it in your new Map:
Map<String, CustomItem> newMap = new HashMap<>();
for (Map.Entry<String, CustomItem> entry : db) {
String key = entry.getKey();
CustomItem value = entry.getValue()
value = value.clone();
newMap.put(key, value);
}
A shorter way:
Map<String, CustomItem> newMap = new HashMap<>(db);
newMap.replaceAll((k, v) -> v.clone());

How to copy a List of key value except 1 key in Java?

I have 1 List of key:value pair primaryList in Java, now i want to copy complete list except one key date in primaryListExceptDate. Can anyone help me on this? I know we can do it using for loop but i want to know is there any other efficient way of doing it?
So as I understand you, you have a list of Record objects that keep pairs of values as key, value!?
Then you can use Stream api to do what you want. Something like:
List<Record> primaryListExceptDate = primaryList.stream()
.filter(record -> !record.getKey().equals(unwantedDateInstance))
.collect(Collectors.toList());
That will give you a new list without the Record with that unwanted date.
UPDATE: You asked for a Vector example.
I made this test which works fine, d2 is removed. Vector implements List so it can be cast. Collectors doesn't have a toVector method since Vector is outdated:
public class Testa {
public static void main(String[] args) {
Date d1 = new Date(100,1,2);
Date d2 = new Date(101,2,3);
Date d3 = new Date(102,3,4);
Date test = new Date(101,2,3);
Vector<Record> primaryList = new Vector<>();
primaryList.add(new Record(d1, new Object()));
primaryList.add(new Record(d2, new Object()));
primaryList.add(new Record(d3, new Object()));
List<Record> primaryListExceptDate = primaryList.stream()
.filter(record -> !record.getKey().equals(test))
.collect(Collectors.toList());
primaryListExceptDate.forEach(r -> System.out.println(r.getKey().toString()));
}
static class Record {
Date key;
Object value;
public Record(Date k, Object v) {
this.key = k;
this.value = v;
}
public Date getKey() {
return key;
}
}
}
Try a foreach construct to copy data into another List with an if to exclude the object that not interest to you. You may think it's not such an efficient way, but most of the methods the API offer have O(n) complexity.
I think it's the simplest method. You also could use List proper methods to copy the List and then remove the object, but this could be a bit more onerous if you look at performance.
Anyway, I suggest you to use Map Collection: it comes to rescue of you when using a key:value pair and it's very efficient! List is useless in this case.
I don't know if it's more efficient but you could first make a copy of the list and then iterate over it with an iterator and remove the entry with key 'date'.
EDIT: something like this:
List<Record> primaryList = ...;
List<Record> primaryListExceptDate = new ArrayList<>(primaryList );
Iterator<Record> it = primaryListExceptDate.iterator();
while(it.hasNext()) {
Record record = it.next();
if (record.getKey().equals("date")) {
it.remove();
}
}

HashMap with different possible value type

Imagine a situation where you have something like this :
class AbstractClass{void sharedMethod()}
class ClassOne extends AbstractClass{void sharedMethod(); void specificMethod()}
class ClassTwo extends AbstractClass{void sharedMethod(); void specificMethod2()}
I want to store in a Map something like this :
HashMap<String, List<SOMETHING_I_DON'T_KNOW_IN_ADVANCE>> hm = new HashMap<>();
hm.put("blabla", List<ClassOne>);
hm.put("blublu", List<ClassTwo>);
I know that I can put AbstractClass as the value type for my map but I want to have access to my specific methods. I already did some research to try to dynamic cast my object when I get them, but I didn't find anything satisfying.
Thanks in advance
Would creating "abstract void specificMethod()" in your AbstractClass, and then making concrete versions of specificMethod() in your concrete classes work for you?
That way you don't need to worry about whether to call specificMethodOne() or specificMethodTwo() ... the AbstractClass object already knows which version of specificMethod() it's supposed to use.
I know that I can put AbstractClass as the value type for my map but I want to have access to my specific methods.
I am assuming that you want to do this:
List<ClassOne> one = hm.get("blabla");
one.specificMethod();
// or
List<ClassTwo> two = hm.get("blublu");
two.specificMethod2();
Unfortunately you can't do this automatically because of type erasure. The Map and Lists have no idea what types they are storing so there is no way for them to help here. To them you are storing Objects.
Storing different types of objects in a Collection is a difficult pattern to support correctly and requires you to do your own casting once you get the values out of the Map.
List<AbstractClass> list = hm.get("blabla");
if (list instanceof ClassOne) {
ClassOne one = (ClassOne)list;
one.specificMethod();
} else if (list instanceof ClassTwo) {
ClassTwo two = (ClassTwo)list;
two.specificMethod2();
}
...
Yes this is gross and error prone but there is no easy way to do this using Java generics.
I would consider either encapsulating the data in a class, such as
private static class MyMessages
{
final List<ClassOne> typeOneMessages = new ArrayList<>();
final List<ClassTwo> typeTwoMessages = new ArrayList<>();
public void addTypeOne(ClassOne c) {
typeOneMessages.add(e);
}
// add appropriate getter methods; or a stream
public Stream<ClassOne> streamOne()
{
return typeOneMessages.stream();
}
// same for type two
}
Or follow what Rick Stabile suggested, and have a single method defined in the abstract class.
If you wish to keep them together, you will have to cast on retrieval, such as:
public static void main(String[] args)
{
final Map<String, List<? extends AbstractClass>> map = new HashMap<>();
List<ClassOne> ones = new ArrayList<>();
for (int i = 0; i < 4; i++) {
ones.add(new ClassOne());
}
List<ClassTwo> twos = new ArrayList<>();
for (int i = 0; i < 8; i++) {
twos.add(new ClassTwo());
}
map.put("ones", ones);
map.put("twos", twos);
List<? extends AbstractClass> out = map.get("ones");
// can call shared method on the contents
out.stream().forEach(a -> a.sharedMethod());
// will have to cast to the specific type
out.stream().filter(a -> a instanceof ClassOne)
.forEach(c1 -> ((ClassOne)c1).specificMethod());
List<? extends AbstractClass> out2 = map.get("twos");
// can call shared method on the contents
out2.stream().forEach(a -> a.sharedMethod());
// will have to cast to the specific type 2
out2.stream().filter(a -> a instanceof ClassTwo)
.forEach(c2 -> ((ClassTwo)c2).specificMethod2());
}
You can achieve that by using "wildcard(?) arguments" and because of the "Type Safety" mechanism a cast is inevitable.
This works for me:
HashMap<String, List<? extends AbstractClass>> hm = new HashMap<>();
ClassOne class1 = new ClassOne();
ClassTwo class2 = new ClassTwo();
List<ClassOne> c1l = new ArrayList<>();
c1l.add(class1);
List<ClassTwo> c2l = new ArrayList<>();
c2l.add(class2);
hm.put("blabla", c1l);
hm.put("blublu", c2l);
#SuppressWarnings("unchecked")
List<ClassOne> xyy = (List<ClassOne>)hm.get("blabla");
ClassOne c1 = xyy.get(0);
c1.specificMethod1();
and my output was:
Hello! from SpecificMethod1

How to reference a variable using a string

I am attempting to reference a variable using a certain string but have no idea how to do it. I know that I can use if statements if I really had to but I am sure that there is a simple way. An example is a Integer named dog. I would try to access the Integer using another string that contained the text dog.
private int dog;
String anything = "dog";
Is there anyway this is possible? Thanks!
Try this:
// use a map for referring to a value given its name
Map<String, Integer> vars = new HashMap<String, Integer>();
// for example, let's use these values
String anything = "dog";
int dog = 10;
// bind a value to a name
vars.put(anything, dog);
// retrieve the value, given its name
vars.get(anything);
=> 10
http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
public static void main(String[] args) {
Map<String, MyObject> mapping = new HashMap<>();
}
Or new HashMap<String, MyObject>(); for pre java 7
You should use a Map of String to Integer. For example,
public static void main(String[] args) {
java.util.Map<String, Integer> dogMap = new java.util.HashMap<String, Integer>();
dogMap.put("Snoop", 10);
dogMap.put("doggy", 15);
dogMap.put("dog", 20);
System.out.println(dogMap);
}
Which outputs
{doggy=15, Snoop=10, dog=20}
Two options: create a Map<String, Object> that connects the two, or use reflection. I prefer reflection.
in order to get the field:
public class Test {
private int dog = 10;
private String anything = "dog";
public static void main(String[] args){
Test obj = new Test();
Object field = obj.getClass()
.getDeclaredField(obj.anything)
.get(obj);
System.out.println(field);
}
}
Output:
10
Create an object of the class that you will use. Then use the getDeclaredField() method on the class of that object. This will look into the private fields that are set, getField() holds only the public fields. That's it.
I've removed the try-catch from the post because it just clutters it.

Determining an object's variable name dynamically?

Let's say I have some objects:
ArrayList<SomeObject> list = new ArrayList<SomeObject>();
SomeObject A = new SomeObject();
SomeObject B = new SomeObject();
SomeObject C = new SomeObject();
SomeObject D = new SomeObject();
These constructors automatically add each object to the ArrayList so I can iterate over them but still maintain the variable names for direct access:
public SomeObject(){
// init stuff here
list.add(this);
}
But then, let's say I want to output some debug info, and iterate through list and print out the NAME of each object? How can I do that? Essentially, when "SomeObject A = new SomeObject();" is executed, I want to use reflection (if possible) to determine that this variable's name is "A" (a String) and either store that in the object when the constructor executes, or determine it dynamically through reflection when referencing this object with the variable named "A". Does that make sense? How can I do this?
Thanks!
The compiler doesn't keep the variable names you define in your code, so this isn't possible at run-time.
I don't see why you insist on working with List, as it seems that what you're looking for is a Map. An entry in a map is a named object, e.g. it has a key used to look up the entry in the map.
Map<String,SomeObject> map = new HashMap<String,SomeObject>();
map.put("A",new SomeObject());
map.put("B",new SomeObject());
If your objects have names or need to know their names, then the object should have the name as a property. Using variable names or map keys for object identification is not good.
Map<String,SomeObject> map = new HashMap<String,SomeObject>();
map.put("A",new SomeObject("A"));
map.put("B",new SomeObject("B"));
However, this is repetitive and you may want to refactor that into a more expressive design by introducing new classes:
SomeObjects objects = new SomeObjects();
SomeObject objectA = objects.create("A");
SomeObject objectB = objects.create("B");
// The container can manage references if you like to
SomeObject objectA = objects.get("A");
SomeObjects may use a Map internally to manage the objects:
class SomeObjects {
Map<String,SomeObject> objects = ...;
public SomeObject create(String name) {
SomeObject newObject = new SomeObject(name);
objects.put(name,newObject);
return newObject;
}
public SomeObject get(String name) {
return objects.get(name);
}
}
To iterate over either the object names or over the objects, the container can simply provide iterators for the keys of the map or the values of the map:
public class SomeObjects {
Map<String,SomeObject> objects = ...;
public Iterator<SomeObject> objects() {
return objects.values().iterator();
}
public Iterator<String> names() {
return objects.keySet().iterator();
}
}
To use these iterators, you can do:
public void test() {
SomeObjects objects = ...;
for(SomeObject obj : objects.objects()) {
// Do something with the object
}
for(String objName : objects.names()) {
// Do something with the object name
}
}
If you directly use a Map, you can use the Map's Entry class, which is a key and value pair:
public void test() {
Map<String,SomeObject> objects = new HashMap<String,SomeObject>();
objects.put("A",new SomeObject());
for(Entry entry : objects.entrySet()) {
System.out.printlnt("Processing object with name: " + entry.getKey());
SomeObject obj = entry.getValue();
doSomethingWith(obj);
}
}
you could just create a String field in SomeObject called name and store the name there.
OR
As much as I hate this answer:
Assuming you are creating the same objects every time, you could create a method which checks references, like so:
public void referenceChecker(SomeObject thing){
if( A == thing) System.out.println("A");
else if(B == thing) System.out.println("B");
//etc etc
}
Its not pretty and it's annoying to maintain, but it works

Categories