I am trying to optimize below code snippet. I want to clear multiple session keys in java by using a single loop. So the requirement is that I don't want to clear all the session keys, I want to keep some of the session keys. For example, in below code snippet, I am trying to remove keys containing ID_NAME_ and keep the one with the id passed to the method.
Below is the code snippet I wrote which works fine:
private void clearPreviousIdFromSession(HttpServletRequest request, String id) {
HttpSession session = request.getSession();
Enumeration keys = session.getAttributeNames();
ArrayList<String> keyArrs = new ArrayList<>();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
System.out.println("Keys for session : " + key);
if (key.contains("ID_NAME_"+id)) {
continue;
} else if(key.contains("ID_NAME_")) {
keyArrs.add(key);
}
}
for(String k : keyArrs){
System.out.println(k);
session.setAttribute(k, null);
session.removeAttribute(k);
}
}
Below is the code I was trying to make it into a single loop, but getting an error because removeAttribute() will remove the object bound with the specified name from this session. In this case, I think key will be removed and won't be able to check for the nextElement in the list.
private void clearPreviousIdFromSession(HttpServletRequest request, String id) {
HttpSession session = request.getSession();
Enumeration keys = session.getAttributeNames();
//ArrayList<String> keyArrs = new ArrayList<>();
String key;
while (keys.hasMoreElements()) {
key = (String) keys.nextElement();
System.out.println("Keys for session : " + key);
if (key.contains("ID_NAME_" + id)) {
continue;
} else if (key.contains("ID_NAME_")) {
session.removeAttribute(key);
}
}
}
Any suggestions to improve my code. I have tried looking here and some old questions of stack overflow questions and here, but still having difficulty. TIA
Your want to optimize something that is already well designed.
As you should not remove elements during iteration of them, you collect elements to delete into a List and after iteration you iterate on this List to delete them.
This approach is nice and the logic flow is "optimized".
If you had to really change something in the actual code, it could be the redundant operation :
session.setAttribute(k, null);
session.removeAttribute(k);
You should choose only one of them :
for(String k : keyArrs){
System.out.println(k);
session.removeAttribute(k);
}
since session.setAttribute(k, null) has the same effect as calling session.removeAttribute(k).
if (key.contains("ID_REPO_" + id)) {
continue;
} else if (key.contains("ID_REPO_")) {
session.removeAttribute(key);
}
refactoring this to java stream:
key.stream()
.filter(key -> !key.contains("ID_REPO_" + id)) //filter all keys that do not have string
.forEach(key -> session.removeAttribute(key)) // execute lambda on every key filtered
Related
When I wrote this piece of code due to the pnValue.clear(); the output I was getting was null values for the keys. So I read somewhere that adding values of one map to the other is a mere reference to the original map and one has to use the clone() method to ensure the two maps are separate. Now the issue I am facing after cloning my map is that if I have multiple values for a particular key then they are being over written. E.g. The output I am expecting from processing a goldSentence is:
{PERSON = [James Fisher],ORGANIZATION=[American League, Chicago Bulls]}
but what I get is:
{PERSON = [James Fisher],ORGANIZATION=[Chicago Bulls]}
I wonder where I am going wrong considering I am declaring my values as a Vector<String>
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = (LinkedHashMap<String, Vector<String>>) annotationMap.clone();
pnValue.clear();
}
EDITED CODE
Replaced Vector with List and removed cloning. However this still doesn't solve my problem. This takes me back to square one where my output is : {PERSON=[], ORGANIZATION=[]}
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (List<String>) pnValue;
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = pnValue;
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = annotationMap;
}
pnValue.clear();
You're trying a bunch of stuff without really thinking through the logic behind it. There's no need to clear or clone anything, you just need to manage separate lists for separate keys. Here's the basic process for each new value:
If the map contains our key, get the list and add our value
Otherwise, create a new list, add our value, and add the list to the map
You've left out most of your variable declarations, so I won't try to show you the exact solution, but here's the general formula:
List<String> list = map.get(key); // try to get the list
if (list == null) { // list doesn't exist?
list = new ArrayList<>(); // create an empty list
map.put(key, list); // insert it into the map
}
list.add(value); // update the list
I want to remove all entries from a LinkedHashMap that were added after an entry with a given key.
My first try was:
LinkedHashMap<String, SomeObject> var = new LinkedHashMap<String, SomeObject>();
public void removeEntriesAfter(String key) {
boolean deleteEntries = false;
for (String currentKey : var.keySet()) {
if(deleteEntries) {
var.remove(currentKey);
} else {
if(key.equalsIgnoreCase(currentKey)) {
// Do not remove the current entry
deleteEntries = true;
}
}
}
}
But then I received a java.util.ConcurrentModificationException.
My second idea was to first determine the keys, an remove them afterwards.
public void removeEntriesAfter(String key) {
boolean deleteEntries = false;
List<String> listOfEntriesToBeRemoved = new ArrayList<String>();
// Determine entries to be deleted
for (String currentKey : var.keySet()) {
if(deleteEntries) {
listOfEntriesToBeRemoved.add(currentKey);
} else {
if(key.equalsIgnoreCase(currentKey)) {
// Do not remove the current entry
deleteEntries = true;
}
}
}
// Removed selected entries
for (String currentKey : listOfEntriesToBeRemoved) {
var.remove(currentKey);
}
}
That works, but I'm sure there is a more elegant/efficient way of doing this.
To avoid a ConcurrentModificationException you can use an Iterator.
Iterator<String> it = map.keySet().iterator();
while (it.hasNext())
if (it.next().equalsIgnoreCase(currentKey))
break;
while (it.hasNext()) {
it.next();
it.remove();
}
If you wanted the most efficient solution, it would be to go straight to the appropriate entry in the first place. To do this you would have to only ever put lower case keys into the map (rather than putting any old strings and comparing using equalsIgnoreCase). Then, using reflection, you could access the Map.Entry object corresponding to currentKey.toLowerCase() and then, using reflection again, you could follow the links all the way through the map. None of this is possible without reflection because neither the entry corresponding to a key nor the links between entries are exposed through public API. I do not recommend reflection as your code could easily break in the future if the code for LinkedHashMap is changed.
Lets say you have an Iterator which will contains values that you need to compare with values that are located in a separate List.
Iterator<Map.Entry<String, Object>> it = aObj.items();
while (it.hasNext()) {
Map.Entry<String, Object> item = it.next();
nameValue = item.getNameValue();
keyValue = item.getKeyValue();
System.out.println("Name: " + nameValue);
System.out.println("Value: " + keyValue);
}
This outputs:
Name: header
Value: 22222
Lets say you have a separate list (in which you want to compare the above values with):
List<Items> items = new ArrayList<>();
for (Item item : items) {
itemNameValue = item.getName();
itemKeyValue = item.getKey();
System.out.println("Name: " + itemNameValue);
System.out.println("Value: " + itemKeyValue);
}
This outputs:
Name: header
Value: 44444
Since these are different types of loops (one is a while loop and the other one is a for each loop)
how can you compare for example:
if (nameValue.equals(itemNameValue())) {
// do something?
}
I need to iterate over both collections / data structures at the same time...
Would this be the solution?
String nameValue = "";
Object keyValue = "";
String itemNameValue = "";
String itemKeyValue = "";
Iterator<Map.Entry<String, Object>> it = aObj.items();
while (it.hasNext()) {
Map.Entry<String, Object> item = it.next();
nameValue = item.getNameValue();
keyValue = item.getKeyValue();
for (Item item : items) {
itemNameValue = item.getName();
itemKeyValue = item.getKey();
}
if (nameValue.equals(itemNameValue())) {
// do something?
}
}
Basically, what I am trying to ask (in a very simplified way is this):
(1) The collection that needs to be iterated in a while loop is just test input (sample data)
(2) The array list from the second collection is really a list of data which was returned from a database call (DAO) and placed into the ArrayList.
I am trying to verify if the input from Iterator inside the while loop is the same as the values from the ArrayList (which came from a database). Since these are different data structures requiring different looping mechanisms. How could I iterate through both data structures at the same time and compare them? The second data structure (the array list) is the actual set of values that are correct.
I don't know if there's a guarantee that each iteration would be comparing the same items if I use a nested loop?
Thank you for taking the time to read this...
The problem you are facing is a direct result of a BAD Application design.
The underline incorrect assumption of this question is that the map and the list will hold the objects in the same sequence.
List --> A data structure that is ordered by not sorted
Map --> A data structure that is neither ordered nor sorted
This is not to say that these two data structures don't work well together. However, using them to store the same list should only result from an awkward program design.
Even though to answer your question, you can use the below code to accomplish this:
Iterator<Map.Entry<String, Object>> it = aObj.items();
List<Items> items = dbCall.getItems(); // Get the list of Items from the DB
int index = 0;
while (it.hasNext()) {
Map.Entry<String, Object> itemFromMap = it.next();
Item itemFromList = items.get(index);
if(itemFromMap.getNameValue().equals(itemFromList.getName()) &&
itemFromMap.getKeyValue().equals(itemFromList.getKey())){
// If you prefer a single .equals() method over &&, then you can implement a Comparator<Item>
return false;
}
index++;
}
return true;
I am using myBatis 3.2.x and have run into a scenario where I need to do multiple table inserts in one database trip,
I was wondering if I can create a master INSERT sql mapper file which would call these multi table inserts and save me network trips
I am consuming JSON objects from a EMS server and my Turn around time is a bit higher then required.
All suggestions and hints are welcome.
Thanks
VR
Use Collections.sort() to sort and use a simple for cycle to catch doubles, e.g.:
Collections.sort(myList);
A previous = null;
for (A elem: myList) {
if (elem.compareTo(previous) == 0) {
System.err.println("Duplicate: "+elem);
}
previous = elem;
}
Assuming that the Comparable is consistent with the equals implementation, you can use a Set. You can add each element to a Set using Set.add(..) and use the return value of add to determine if the value was already present in the Set and create either a Set or a List to return.
Note: If you need each duplicate returned only once, you can change the return list to a set.
List<A> duplicates(List<A> myList) {
Set<A> s = new HashSet<A>();
List<A> duplicates = new ArrayList<A>(); // change to using a Set if you want to report each duplicate item only once.
for (A item: myList) {
if (!s.add(item)) {
duplicates.add(item);
}
}
return duplicates;
}
An improved version using sorting (to report duplicate elements only once, I assume there are no null values in the list):
Collections.sort(myList);
A previous = null, elem = null;
for (java.util.Iterator<A> it = myList.iterator; it.hasNext(); elem = it.next()) {
if (elem.compareTo(previous) == 0) {
System.err.println("Duplicate: "+elem);
while (it.hasNext() && (elem = it.next()).compareTo(previous)) {
//loop through other values
}
}
previous = elem;
}
A version using SortedSet (probably this is faster a bit): and corrected the same
SortedSet<A> set = new TreeSet<>(), duplicates = new TreeSet<>();
for (A a: myList) {
if (!set.add(a)) {
duplicates.add(a);
}
}
return duplicates;//or for (A a: duplicates) System.println("Duplicate: " + a);
I'm trying find the most popular word in an array using Hashtables. For some reason the while loop is looping infinitely. I've debugged and the element never changes from the first one it gets. Any ideas on why this is happening?
Here is my code:
import java.util.Hashtable;
public class MyClass {
public String mostPopularString (String []words) {
if (words == null)
return null;
if (words.length == 0)
return null;
Hashtable<String, Integer> wordsHash = new Hashtable<String, Integer>();
for (String thisWord : words)
{
if (wordsHash.containsKey(thisWord))
{
wordsHash.put(thisWord, wordsHash.get(thisWord) + 1);
}
else
{
wordsHash.put(thisWord, 1);
}
}
Integer mostPopularCount = 0;
String mostPopularWord = null;
boolean tie = false;
while (wordsHash.keys().hasMoreElements())
{
String currentWord = (String) wordsHash.keys().nextElement();
if (wordsHash.get(currentWord) > mostPopularCount)
{
mostPopularCount = wordsHash.get(currentWord);
mostPopularWord = currentWord;
tie = false;
}
else if (wordsHash.get(currentWord) == mostPopularCount)
{
tie = true;
}
}
if (tie)
return null;
else
return mostPopularWord;
}
}
You're calling wordsHash.keys() on each iteration of the loop, which gives you a fresh Enumeration<String> on each iteration - you're then calling it again inside the loop.
You want to call it once, and then iterate over the single Enumeration<String>:
Enumeration<String> iterator = wordsHash.keys();
while (iterator.hasMoreElements())
{
String currentWord = iterator.nextElement();
...
}
Note that as you're also getting the value for each element, you'd be better off iterating over the entrySet() rather than the keys().
You'd also be better off using HashMap instead of Hashtable, as then you could just use an enhanced for loop...
The problem is in line
while (wordsHash.keys().hasMoreElements())
each time through the loop, you are getting a new copy of the enumeration. You'll want to get the keyset once, and iterate over that.
It would probably be easier to use an enhanced for Loop here as well
for (Map.Entry<String,Integer> entry : wordsHash.entrySet()) {
String currentWord = entry.getKey();
Integer currentCount = entry.getValue();
//more code here
}
This should provide the behavior you want, while being simpler and easier to read.
The problem is that whenever you call wordsHash.keys(), it returns a new enumeration:
while (wordsHash.keys().hasMoreElements()) // <=== HERE
{
String currentWord = (String) wordsHash.keys().nextElement(); // <=== AND HERE
What you need to do is create a single enumeration and use it throughout the loop.
P.S. Why are you using Hashtable and not HashMap?
Every call to .keys() returns a new enumeration, with a new internal pointer for iterating:
Hashtable table = new Hashtable();
table.put("a", "a");
table.put("b", "b");
boolean b = table.keys() == table.keys();
System.out.println(b); // false
// the two calls to `.keys()` returned different instances of Enumeration
So assign your keys enumeration to a variable:
Enumeration keys = wordsHash.keys();
while (keys.hasMoreElements())
{
String currentWord = (String) keys.nextElement();
}
Change your code to:
Enumeration<String> keys = wordsHash.keys();
while (keys.hasMoreElements()) {
String currentWord = keys.nextElement();
So that a new enumeration pointing to the first key of the HashTable is not created every time that you enter the loop.
Nothing is modifying the wordsHash. That means that if wordsHash.keys().hasMoreElements() is true once, it'll continue to be true for the rest of the program. This causes an infinite loop. You either need to remove the keys as you go along or you should just use a for
you get a new Iterable ofer all keys each loop iteration: wordsHash.keys() as long as there is at least one key in it the while loop never ends.
Replace:
while (wordsHash.keys().hasMoreElements()){
String currentWord = (String) wordsHash.keys().nextElement();
by
for (String currentWord: wordsHash.keys()){
Also, unrelated to your Enumeration issue, this is probably a defect:
else if (wordsHash.get(currentWord) == mostPopularCount)
That's a reference comparison of a java.lang.Integer to another java.lang.Integer. It is not a comparison of the actual values they represent. It is working for "small" numbers because auto-boxing uses cached references, but will eventually break. You probably want:
else if (wordsHash.get(currentWord) == mostPopularCount.intValue())