I am trying to add value for the List which is stored in HashMap and that has one parent List.
When I try to do so I get "The method get in type is not compatible with the List"
I am trying the following code, logic is :
If I get the matching value of tID in the txnValue List I am just adding the "Values" List otherwise I am creating the new HashMap.
List < HashMap > txnvalues = new ArrayList < HashMap > ();
for (LinkedHashMap < String, Object > linkedHashMap: resultset) {
HashMap data = new HashMap < > ();
HashMap attrData = new HashMap < > ();
List values = new ArrayList < > ();
data.put("values", new ArrayList < > ());
attrData.put("attrID", linkedHashMap.get("ID"));
attrData.put("attrVal", linkedHashMap.get("VAL"));
String txnID = linkedHashMap.get("T_ID").toString();
if (!txnvalues.stream().anyMatch(list -> list.containsValue(txnID))) {
data.put("tID", linkedHashMap.get("T_ID"));
values.add(attrData);
data.put("Values", values);
txnvalues.add(data);
} else {
txnvalues.get("Values").add(attrData); // this Line throws error
}
}
Example :
[{
"tID":123,
"Values":[{attrID:1,attrVal:123}]
}]
//Here If linkedHashmap.get("T_ID") = 123 which matches with tID then I want to add data in the Values
[{
"tID":123,
"Values":[{attrID:1,attrVal:123},{attrID:11,attrVal:467}]
}]
//If it doesn't match then I want to create new Hashmap and update txnValues Like this
[{
"tID":123,
"Values":[{attrID:1,attrVal:123},{attrID:2,attrVal:3435}]
},
{
"tID":456,
"Values":[{attrID:2,attrVal:233}]
}
]
I decided to parameterize all of your various iterables. Below is the parameterized code.
List<HashMap<String, List<HashMap<String, Object>>>> txnvalues = new ArrayList<HashMap<String, List<HashMap<String, Object>>>>();
for (LinkedHashMap<String, Object> linkedHashMap : resultset) {//Error here
HashMap<String, List<HashMap<String, Object>>> data = new HashMap<String, List<HashMap<String, Object>>>();
HashMap<String, Object> attrData = new HashMap<String, Object>();
List<HashMap<String, Object>> values = new ArrayList<HashMap<String, Object>>();
data.put("values", new ArrayList<>());
attrData.put("attrID", linkedHashMap.get("ID"));
attrData.put("attrVal", linkedHashMap.get("VAL"));
String txnID = linkedHashMap.get("T_ID").toString();
if (!txnvalues.stream().anyMatch(list -> list.containsValue(txnID))) {
data.put("tID", linkedHashMap.get("T_ID")); //Error here
values.add(attrData);
data.put("Values", values);
txnvalues.add(data);
} else {
txnvalues.get("Values").add(attrData); //Error here
}
}
First, you have multiple errors in your code such as trying to put a String key and Object value into data, which is a HashMap that only takes a String key and a List(of HashMaps of Strings and Objects) value. Another such is trying to get an item from txnvalues by a String, when txnvalues is a List and therefore requires an integer index parameter.
Second, you have a variable here which is never defined: resultset. We don't know what it is or how it is used, since it's never referenced elsewhere.
Third, there are many many ways to handle nested sets. This >-> List<HashMap<String, List<HashMap<String, Object>>>> is simply horrible.
Please re-write your code in a way that is readable, parameterized, and can properly compile without errors. Just parameterizing will help you keep track of which iterables take which parameters and will help prevent the problem you had when you came here for help.
I'm probably late with this answer. Nevertheless, I'll introduce a possible remedy accompanied by a detailed explanation.
At the first glance, such a deeply nested collection seems contrived and incomprehensible. But problems that you can see in this code aren't something unusual, they could be observed in many questions on StackOverflow, and in many repositories. The only difference is in concentration.
Let's try to examine it closely. A map is a data structure that is commonly misused by beginners because it allows to combine objects of different nature. I am pretty sure that provided code models something more or less tangible. Did you notice that PO tries to access an entry that has a string key called "id"? That's a clear indicator that collections here are used in place of objects.
If I say object graph can be far more complex, it probably wouldn't be something new. But how to reason about the code that is written in such a way?
Let's step aside for a moment and consider the following task:
there are a number of sailboats, you need to determine which of them will win the race and return its name as a result;
input provided as a plain text and consists of the following parameters: unique name, displacement, and weight (only these three for simplicity);
the speed of the vessel depends on its displacement and weight (i.e. formula is provided, we need only parse the values);
It is very likely that somebody can come up with such a solution:
create a Map<String, List<Double>>, where the key is a sailboat's name and the value is a list that contains displacement and weight;
then just iterate over the entry set, apply the formula and so find the fastest vessel.
Only a couple of methods, and it seems that a separate class for a sailboat will allegedly increase the overall complexity and amount of code. That's a common delusion for many students. The creation of a separate class will provide a logical structure to the code and will pay off if you would wish to extend or reuse it. Note that not only attributes of the sailboat must belong to this class but also the methods that allow to compute sailboat's speed and compare sailboats based on it.
Decomposition is a skill and it has to be exercised. And for those of you who didn't realize from the beginning that a sailboat in the previous example has to be represented by an object, I advise to try the next exercise: describe a university, a candy shop, a grocery store, a cat, anything you like but without using objects. First, think about a couple of use-cases that entail accessing some properties of the elements of the system that you're trying to model. Then draw diagrams and write the code using warriors collections and arrays, pay attention that the more complex your system becomes, the more cumbersome become all nested maps and lists, which make you write your code like this:
map.get(something).get(something).add(somethingElse);
And then, when you see the problems, you are ready to implement the classes that make sense in your domain model and compare the two approaches.
Disclaimer: understanding decomposition is a crucial thing but class design is a very broad topic, there are lots of things to study in this area like classic principles and design patterns. But before diving into these topics, you have to have a firm understanding of decomposition and OOP. Without this knowledge even with an object-oriented approach, your solution could become convoluted and difficult to manage. But this is a step in the right direction. The fact alone that you are using an object-oriented language doesn't automatically make your solution object-oriented. It's a skill, and it has to be exercised.
It was a very long digression, now let's get to the point.
As I already said, I'm convinced that the post author had in mind some kind of natural use case. Instead of names that describe the system in this maze of data structures we can see only dump get() and put(). But there's a clue in the usage of map. An id as a key is a clear indicator that it has to be an object which is substituted by a map.
That is a start of a journey, I'll try to provide a scenario that makes sense (at least a bit) and pieces of a system that fits into a structure depicted in the scheme provided at the start of this post.
Let's consider an organization that sells something (I'm not trying to guess what was the author's intention, but providing a use case that will allow to reason about the code). There are a bunch of departments, each with a unique identifier.
Each department has a collection of products that it sells. Department gets different products from different suppliers. And in turn, each product has a unique id a collection of suppliers represented by plain string (it looks contrived, but keep in mind it's just an illustration of what the code does).
As a use-case, let's assume that the company launches a new product and it must be accessible in all its departments. The code checks whether the department has this product already, if not, the product will be added with a default set of suppliers, otherwise it merges the existing set of suppliers and the default one.
As you can see the code in the main method is very concise. Note that all the miscellanies of data structures are still there, but we are not accessing them directly. As the information expert principle suggests, this logic is hidden inside the objects. That makes this solution reusable and less error-prone.
public static void main(String[] args) {
// this list is a rough equivalent of the "List<Map<String, List<Map<String, Object>>>> txnvalues"
List<Department> departments =
List.of(new Department("dep11"), new Department("dep12"));
Product newProd = new Product("id123"); // a NEW Product with id = "id123"
newProd.addAllSuppliers(List.of("supplierA", "supplierB"));
for (Department dep: departments) { // launching the new Product
dep.mergeProduct(newProd);
}
}
public class Department {
private final String departmentId;
private final Map<String, Product> idToProduct;
public Department(String departmentName) {
this.departmentId = departmentName;
this.idToProduct = new HashMap<>();
}
public void mergeProduct(Product prod) {
idToProduct.merge(prod.getId(), prod, Product::merge);
}
public void mergeAllProducts(Iterable<Product> products) {
for (Product prod: products) {
mergeProduct(prod);
}
}
public void addProduct(Product prod) {
idToProduct.put(prod.getId(), prod);
}
public void addAllProducts(Iterable<Product> products) {
for (Product prod: products) {
addProduct(prod);
}
}
public String getId() {
return departmentId;
}
public Map<String, Product> getIdToProduct() {
return Collections.unmodifiableMap(idToProduct);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Department other) {
return departmentId.equals(other.departmentId);
} else return false;
}
#Override
public int hashCode() {
return Objects.hash(departmentId);
}
}
public class Product {
private final String productId;
private final Set<String> suppliers;
public Product(String id) {
this.productId = id;
this.suppliers = new HashSet<>();
}
public boolean addSupplier(String newSup) {
return suppliers.add(newSup);
}
public boolean addAllSuppliers(Collection<String> newSup) {
return suppliers.addAll(newSup);
}
public Product merge(Product other) {
if (!this.equals(other)) throw new IllegalArgumentException();
Product merged = new Product(productId);
merged.addAllSuppliers(this.suppliers);
merged.addAllSuppliers(other.suppliers);
return merged;
}
public String getId() {
return productId;
}
public Set<String> getSuppliers() {
return Collections.unmodifiableSet(suppliers);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Product other) {
return this.productId.equals(other.productId);
} else return false;
}
#Override
public int hashCode() {
return Objects.hash(productId);
}
}
Further steps:
First of all make sure that you don't have gaps in the core concepts of OOP: encapsulation, inheritance, and polymorphism.
Draw before you start to code, it's not necessary to create a full-blown UML diagram. Even a rough set of named boxes with arrows will help you understand better how your system is structured and how its parts interact with each other.
Read and apply. Extend your knowledge gradually and try to apply it. High cohesion, Low coupling, SOLID, and lots of helpful reading can be found here, for instance this recent post
Write a bit, test a bit: don't wait until your code became a beast. Write a bit and give it a try, add something else and take a look at how these parts fit together.
In the else block, you call get method of txnvalues which a list of HashMaps and thus it expects an integer index. I believe you assume that at this point you've got a reference to the HashMap to which you would add the values. But you don't.
So, you need to find the index where to add the values, which means you have to look through the txnvalues list again.
For this reason, you should use a different approach:
txnvalues.stream()
.filter(m -> m.get("tID").equals(txnID))
.findFirst()
.ifPresentOrElse(
m -> m.get("Values").add(attrData),
() -> {
HashMap data = new HashMap<>();
// Other stuff to fill the data
txnvalues.add(data);
}
);
Here .filter(m -> m.get("tID").equals(txnID)) corresponds to your .anyMatch(list -> list.containsValue(txnID)) (the parameter list is actually instance of HashMap).
I changed the condition: according to your data sample, you looking for Map which has txnID value for the "tID" key, therefore getting the value of this key is faster than looking through all the values in the HashMap. (It may return null.)
So filter will return only the entries which contain match the required value of the "tID" key. Then .findFirst() “returns” the reference to that HashMap. Now .ifPresentOrElse performs the actions you want:
m.get("Values").add(attrData) into the list; this corresponds your one line of code in the else block;
the other code is what you had in the if block: if nothing is found, create the new instance.
Say I have the following POJO with the necessary methods and stuff to successfully load and save items into a DDB table:
#DynamoDBTable(tableName = "my_ddb_table")
public class MyDDBTableItem {
#DynamoDBHashKey(attributeName="id")
private String id; // primary key
private String a_value;
private Set<String> some_values;
}
I am using dynamoDBMapper.batchload(), so I need to take action if the exception is thrown. I have never worked with DDB before, so I am unsure of what some of their terms mean (e.g. for getKeys(): "The primary key attribute values that define the items and the attributes associated with the items.").
What I currently want to do is just get some collection (list, set, etc.) of the unprocessed primary keys. Is this the right way to do it (I don't know how I'd test it)?
try {
dynamoDBMapper.batchload(itemsToGet)...
} catch (BatchGetItemException e) {
// I could get them as strings or as the instances of the class MyDDBTableItem, but I'll use String here.
List<String> unprocessedKeys = e.getUnprocessedKeys()
.get("my_ddb_table")
.getKeys() // after this is where I am unclear.
.stream()
.map(map -> map.get("id").getS())
.collect(Collectors.toList());
...
}
Since you are using the higher level DynamoDB mapper, you don't actually need to catch that exception. You can specify how unprocessed keys are handled by using a strategy class implementing DynamoDBMapperConfig.BatchLoadRetryStrategy.
By default, the DynamoDB client will use the DefaultBatchLoadRetryStrategy.
// TODO: implement the strategy interface
BatchLoadRetryStrategy myCustomStrategy = ...;
dynamoDBMapper.batchLoad(itemsToGet, new DynamoDBMapperConfig.Builder().
withBatchLoadRetryStrategy(myCustomStrategy))
The KeysAndAttributes class is used to construct a list of items (by specifying their primary keys) and attributes (which columns to fetch) from a DynamoDB table. You can construct an instance of that class like this:
KeysAndAttributes kna = new KeysAndAttributes().withKeys(keysToLoad);
Basically, you would only want to call getUnprocessedKeys() manually if you are working with the lower level API for loading items in batches.
I am implementing a singleton class that must handle multiple threads accessing its data structure at once.
The class has a method that returns true if the data structure already contains myObject and false otherwise. If the object has not been seen then object is added to the data structure.
boolean alreadySeen(MyObject myObject){}
MyObject has two member variables Instant expiration and String id where id acts as my key to decide whether the data structure contains myObject. I cannot change MyObject class. I need to periodically check the expiration of myObjects in the data structure and remove them if they have expired.
So I am looking to use one or more data structures that I can quickly add, delete and search by both expiration and id. I will mostly be adding elements and searching if element exists with the periodic cleanup removing expired elements.
A map like ConcurrentHashMap<id,MyObject> gives me the O(1) insert and delete but it would be O(n) to search through for expired objects.
As mentioned above I cannot change the MyObject class. So I thought about making a wrapper for that class so I can override equals() and hashcode() and then do an ordered set like ConcurrentSkipListSet<MyObjectWrapper>(new ExpComparator()) This would let me order order the set by expiration date and then I could quickly find expired ones on top. However I believe this would be O(log n) for search, delete.
Is there any better structure I could use? And if not am I better off in the long run with the map at O(1) lookup and add but periodic O(n) for delete of expiration? Or better for set with O(log n) of everything?
You lookup,add and remove operation can run at O(1),but it need other cost as follow:
First,it need double memory to store data
Second,Expiration time cannot be very accurate
we need two map,one store object,key is the id and value is object,like Map<id,MyObject> another may to store the relationship between expiration and MyObjects likeMap<Long,List<MyObjects>>,key need to calculate.
Codes:
In order to simple write code i modify your MyObject class:
class MyObject {
private long expiration;
private String id;
}
The other code
private Map dataSet = new ConcurrentHashMap<>();
private Map> obj2Expiration = new ConcurrentHashMap<>();
public boolean alreadySeen(MyObject myObject) {
boolean exist = dataSet.containsKey(myObject.getId());
if (!exist) {
dataSet.put(myObject.getId(),myObject);
Long expirateKey = myObject.getExpiration() / 5000;
List<MyObject> objects = obj2Expiration.get(expirateKey);
if (null == objects) {
objects = new ArrayList<>();
obj2Expiration.put(expirateKey,objects);
} else {
objects.add(myObject);
}
}
return exist;
}
#Scheduled(fixedRate = 5000)
public void remove() {
long now = System.currentTimeMillis();
long needRemode = now /5000 -1;
Optional.ofNullable(obj2Expiration.get(needRemode))
.ifPresent(objects -> objects.stream().forEach(o -> {
dataSet.remove(o.getId());
}));
}
I am trying to create a cache using guava cache library. One my main requirement is that I want to set the cache expiry after the CacheLoader.load(..) function instead of something most of the examples I encountered on the web, like the one below.
LoadingCache<String, MyClass> myCache =
CacheBuilder.newBuilder().maximumSize(MAX_SIZE).expireAfterWrite(10, TimeUnit.Minutes).build(cacheLoader);
The reason for this is that the object retrieved from the database by the CacheLoader.load(...) function contains the expiration data. So I want to use this information instead of some "random" static value.
I want something like this.
LoadingCache<String, MyClass> myCache =
CacheBuilder.newBuilder().maximumSize(MAX_SIZE).build(cacheLoader);
...
CacheLoader meCacheLoder = new CacheLoader<String MyClass>(){
#Override
public MyClass load(String key) throws Exception {
// Retrieve the MyClass object from database using 'key'
MyClass myObj = getMyObjectFromDb(key);
int expiry = myObj.getExpiry();
// Now somehow set this 'expiry' value with the cache
????
return myObj;
}
};
OR
Is there any better option available than Guava cache for this purpose?
There is no such feature in Guava, as Louis already pointed out.
For example you can use EHCache or cache2k. For cache2k I can give you quick directions since this is a core feature we use regularly:
You can either implement the interface ValueWithExpiryTime on your value object, which is:
interface ValueWithExpiryTime {
long getCacheExpiryTime();
}
Or, you can register a EntryExpiryCalculator to extract the time value. The cache is build as follows:
Cache<Key, Value> cache =
CacheBuilder.newCache(Key.class, Value.class)
.expiryCalculator(new EntryExpiryCalculator<Key, Value>() {
#Override
public long calculateExpiryTime(
final Key key, final Value value,
final long loadTime, final CacheEntry<Key, Value> oldEntry) {
return value.getExpiry();
}
}
)
.build();
The time is the standard long type represented in milliseconds since the epoch. By default the expiry will happen not exactly at the specified time, but zero or a few milliseconds later, depending on your machine load. This is the most effective mode. If this is a problem, add sharpExpiry(true).
Disclaimer: I am the author of cache2k....
so I have been able to put objects into my hash map successfully, but I'm having trouble returning an object. When I used an arrayList for this same project, I simply displayed it with the following method:
public void displayDetails(int currentItem) {
accountIDTextField.setText(table.get(currentItem).getAccountID()+"");
accountNumberTextField.setText(table.get(currentItem).getAccountNumber());
surnameTextField.setText(table.get(currentItem).getSurname());
accountTypeTextField.setText(table.get(currentItem).getAccountType());
}
And pressing the 'first' button would go to the number 1 in the list.
first.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
currentItem = 1;
displayDetails(currentItem);
}
});
As for my hashing, I have used the accountNumber as the key, (hashed by using the % modulo function)
Working backwards, I can get the accountID when I pass in the accountNumber as a parameter in the get() method.
hashMap.get(12345678).getAccountID();
But how do I return the accountID if I just want to get the first object stored in the hash map(i.e get accountID without knowing accountNumber)?
(AccountID is an integer unique to a particular account and will be automatically generated when a new account record is created)
Sorry if this isn't worded very well, I'm still trying to get my head around Java and OOP in general. Any help would be greatly appreciated. Thanks
hope I understood you right. getting only the first item of a HashMap would be something like:
Map<String, String> myhashmap = new HashMap<String, String>();
myhashmap.entrySet().iterator().next();
You can get the contents of the Map by using Map.values().
I would't access the value based on it's order in the map because ordering is not guaranteed. You should give each one a defined number. Then you can access them like:
Object o = map.values().get(id);
to get the first:
Object o = map.values().get(0);