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
Related
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());
Briefly - I try to write a method that should convert the values of String[] sourceAllPeopleInMyFamily to single objects of Person.
Then, check if this Person instance exists in HashMap<String, ArrayList<Person>> allPeople. If it does, I should add this Person instance to HashSet<Person> allPeopleInMyFamily.
Person class contains String personName + int personAge.
HashMap<String, ArrayList<Person>> contains the key String personName + the value array list of Person objects.
I wrote this following code.
Since I use HashMap, I use entry to iterate the all the map.
But since the entry checks all keys&values, this prevents me to have a return statement if the object isn't found.
Please advice is there any better iteration for my case?
boolean addPerson (String[] sourceAllPeopleInMyFamily) {
HashMap<String, ArrayList<Person>> allPeople = pointer to another initialized HashMap in another package;
HashSet<Person> allPeopleInMyFamily = new HashSet<Person>();
for (Map.Entry<String, ArrayList<Person>> entry : allPeople.entrySet()) {
for (String sourcePersonInMyFamily : sourceAllPeopleInMyFamily) {
Person personInMyFamily = new Person (sourcePersonInMyFamily); // Create a new person based on sourceAllPeopleInMyFamily array
if (entry.getValue().contains(personInMyFamily) { // Check if personInMyFamily exists in all people set
if (!allPeopleInMyFamily.add(personInMyFamily))
return false;
// } else { return false; } ???????
}
}
}
}
So I have a class called Test:
public class Test{
protected String name = "boy";
protected String mainAttack = "one";
protected String secAttack = "two";
protected String mainType"three";
protected String typeSpeak = "no spoken word in super class";
//Somehow put all the class variables in an Array of some sort
String[] allStrings = ??(all class' strings);
//(and if you feel challenged, put in ArrayList without type declared.
//So I could put in, not only Strings, but also ints etc.)
public void Tester(){
//Somehow loop through array(list) and print values (for-loop?)
}
}
As you can see, I want to put all the class variables in an Array or ArrayList (or something similar) automatically.
And next I want to be able to loop through the array and print/get the values.
Preferably using an enhanced-for loop.
As other said, don't do this. But this is how:
Class<?> cl = this.getClass();
List<Object> allObjects = new ArrayList<Object>();
for (java.lang.reflect.Field f: cl.getDeclaredFields())
{
f.setAccessible(true);
try
{
Object o = f.get(this);
allObjects.add(o);
}
catch (Exception e)
{
...
}
}
for (Object o: allObjects)
System.out.println(o);
If you really really need do this you need to use Reflection.
However a much better approach would be to store the values in a Map (probably a HashMap) and then you can query/set/etc them from that easily.
You can use Map or Hashmap to store variables and its values instead of Array or Arraylist
HashMap is an object that stores both “key/value” as a pairs. In this article, we show you how to create a HashMap instance and iterates the HashMap data.
Why not use a HashMap for the values and iterate through that?
Iterate through a HashMap
Do this.
String threeEleves = "sky";
String sevenDwarves = "stone";
String nineMortal = "die";
String oneRing[] = new String[] // <<< This
{
threeElves,
sevenDwarves,
nineMortal
}
or do this
// in some class.
public void process(final String... varArgs)
{
for (String current : varArgs)
{
}
}
String one = "noodles";
String two = "get";
String three = "in";
String four = "my";
String five = "belly";
process (one, two, three, four, five);
I have read several posts for this but not getting the exact thing I am looking for. I know how to develop a complex logic for this, this is for Android and we can't expect too much processing on the device due to the limited resources available.
I have an ArrayList of an bean class objects consisting five fields as
Java Bean -- MyShares
fileName
filePath
fileSize
isShared
Creator
I have another ArrayList of String which contains only filepaths. Now what I want is to remove all the common elements between the two arraylist means the file paths in seconds arraylist and file path in first arraylist objects are similar then I have to remove from both of the arraylist but I don't want a new arraylist which contains the uncommon elements. But I want to get my both arraylist only without their common elements.
You could use a Map from String to your object type (I used Obj in order to make a SSCCE).
Assume we are given a list objects and a list strings.
Steps:
Put all objects in a map with their str variable as key
Get all those str variables using map.keySet()
Get all strings that are in objects but not in strings by keys.removeAll(strings)
Get all strings that are in strings but not in objects by strings.removeAll(keys)
Get the objects that correspond to the remaining keys
Note that you need to be careful in steps 3 and 4, because you need to back up one of the collections.
import java.util.*;
public class Test {
public static void main(String[] args) throws Exception {
new Test();
}
public Test() {
List<Obj> objects = new ArrayList<>();
objects.add(new Obj("a"));
objects.add(new Obj("b"));
objects.add(new Obj("c"));
List<String> strings = new ArrayList<>();
strings.add("a");
strings.add("d");
strings.add("e");
remove(objects, strings);
System.out.println(objects);
System.out.println(strings);
}
public void remove(List<Obj> objects, List<String> strings) {
Map<String, Obj> map = new HashMap<>();
for (Obj object : objects) {
map.put(object.str, object);
}
Set<String> keys = map.keySet();
List<String> oldStrings = new ArrayList<>(strings);
strings.removeAll(keys);
keys.removeAll(oldStrings);
objects.clear();
for (String key: keys) {
objects.add(map.get(key));
}
}
public class Obj {
public String str;
public Obj(String str) {
this.str = str;
}
#Override
public String toString() {
return str;
}
}
}
Prints:
[b, c]
[d, e]
Rough Java code:
HashSet<String> commonKeys = new HashSet();
for (Share share : shares) {
commonKeys.add(share.filePath);
}
commonKeys.retainAll(filePaths);
for (Iterator<Share> it = shares.iterator(); it.hasNext(); ) {
Share share = it.next();
if (commonKeys.contains(share.filePath)) {
it.remove();
}
}
filePaths.removeAll(commonKeys);
This won't be O(N) because remove on an ArrayList is expensive. To get O(N) behavior you either need to create new ArrayList instances, or add the elements you don't want removed to temporary lists, and then clear() and add them back into the original lists.
I will go with some clues for you
Suppose you have two lists one for bean objects namely myBeans and another for filePaths namely filePaths
List<MyBean> beansToRemove = new ArrayList<MyBean>();
List<FilePath> filePathsToRemove = new ArrayList<FilePath>();
for(Bean myBean : myBeans) {
for(FilePath filePath : filePaths) {
if(myBean.getfilePath.equals(filePath.getFilePath())) {
beansToRemove.add(myBean);
filePathsToRemove.add(filePath);
}
}
}
//Now remove filePaths and beans if any
for(Bean myBean : beansToRemove) {
myBeans.remove(myBean);
}
for(FilePath filePath : filePathsToRemove) {
filePaths.remove(filePath);
}
it is just a flow to make you clear for what to do; you can further customize it according to your needs.
You can use an outer loop to scan over the Bean objects, and an inner loop to scan over the file paths.
pseudo code:
for (Bean i in beans) {
for (String p in paths) {
if (i.path.equals(p)) {
beansToRemove.add(i);
pathsToRemove.add(p);
}
}
}
beans.removeAll(beansToRemove);
paths.removeAll(pathsToRemove);
I'm not sure if my extra arraylists to track the removed arraylists go against your question or not since the original arrays remain.
If you presort both arrays on the path and keep track of the position in each area (not exhaustive search) you can improve it from n2 to nlgn
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]}