How to optimize nested loops? - java

I have two foreach loops. One of them contains list of unique emails (outer). I would like to have that as outer loop and increase count by one every time there is a match between an element of outer loop and the inner loop.
My code now:
outer: for (String email : emailsOfContactsWhoFitDynConFilter) {
for (Contact contact : emailClicks.items) {
String[] contactLink = (contact.link).split("\\?", -1);
String queryStringActivity = getQueryStringByName("elqTrackId", contactLink[1]);
if (email.equals(contact.EmailAddress) && contactLink[0].equals(linkInDynamicContentSplit[0])) {
if (queryStringActivity !=null && queryStringDynConLink!=null && queryStringActivity.equals(queryStringDynConLink)){
count++;
break outer;
} else if (queryStringActivity == null || queryStringDynConLink == null) {
System.out.println(" - Missing elqTrackId. But base the same, count++");
count++;
break outer;
}
}
}
}
It works, but problem is these two lines:
String[] contactLink = (contact.link).split("\\?", -1);
String queryStringActivity = getQueryStringByName("elqTrackId", contactLink[1]);
Are executed too many times which consumes a lot of time.
I could reverse the loops, so it would look like this:
outer: for (Contact contact : emailClicks.items) {
String[] contactLink = (contact.link).split("\\?", -1);
String queryStringActivity = getQueryStringByName("elqTrackId", contactLink[1]);
for (String email : emailsOfContactsWhoFitDynConFilter) {
if (email.equals(contact.EmailAddress) && contactLink[0].equals(linkInDynamicContentSplit[0])) {
if (queryStringActivity !=null && queryStringDynConLink!=null && queryStringActivity.equals(queryStringDynConLink)){
count++;
break outer;
} else if (queryStringActivity == null || queryStringDynConLink == null) {
System.out.println(" - Missing elqTrackId. But base the same, count++");
count++;
break outer;
}
}
}
}
That would be much faster, but my count++ would happen more times than I want, it wouldn't be +1 per unique email.

There are a couple good options here, but the first would be to simply cache the String[]. This is a valuable lesson in why you should use methods instead of members.
I suggest having a method of contact.getLinkCache() method, implemented something like I have below. This gives you the benefit of not splitting over and over again (there is a clone in there to protect the data, but clone is a pretty fast method, and unless you've identified this as being too slow, you should probably go with this.
class Contact {
String link;
String[] linkSplitCache;
public void setLink(String link) {
this.link = link;
this.linkSplitCache = null;
}
public String getLink() {
return link;
}
public String[] getLinkCache() {
if(linkSplitCache == null) {
linkSplitCache = link.split("\\?",-1);
}
// return linkSplitCache; // could corrupt!
return linkSplitCache.clone(); // pretty fast array copy
}
}
If it is too slow, then you would want some kind of map to cache it in, and this would probably be outside the Contact class.
Map<Contact, String[]> linkSplitCache = new HashMap<>();
outer: for (Contact contact : emailClicks.items) {
String[] contactLink = linkSplitCache.get(contact);
if(contactLink == null) {
contactLink = (contact.link).split("\\?", -1);
linkSplitCache.put(contact,contactLink);
}
// rest of loop here

With a great help from #corsiKlause Ho Ho Ho I could come to the solution:
Map<String, String[]> linkSplitCache = new HashMap<>();
int count = 0;
String[] linkInDynamicContentSplit = linkInDynamicContent.split("\\?", -1);
String queryStringDynConLink = getQueryStringByName("elqTrackId", linkInDynamicContentSplit[1]);
if (emailClicks != null && emailsOfContactsWhoFitDynConFilter != null) {
for (String email : emailsOfContactsWhoFitDynConFilter) {
inner: for (Contact contact : emailClicks.items) {
String[] contactLink = linkSplitCache.get(contact.EmailAddress);
if (contactLink == null){
contactLink = (contact.link).split("\\?", -1);
contactLink[1] = getQueryStringByName("elqTrackId", contactLink[1]);
linkSplitCache.put(contact.EmailAddress, contactLink);
}
if (email.equals(contact.EmailAddress) && contactLink[0].equals(linkInDynamicContentSplit[0])) {
if (contactLink[1] !=null && queryStringDynConLink!=null && contactLink[1].equals(queryStringDynConLink)){
count++;
break inner; // this excludes link clicks which were done
// twice by the same person
} else if (contactLink[1] == null || queryStringDynConLink == null) {
System.out.println(" - Missing elqTrackId. But base the same, count++");
count++;
break inner;
}
}
}
}
}
Basically what I did was adding the link into the HashMap with the unique key Email address, which makes sure that I don't do the same operation more than once where it's not necessary.

Related

Taking data from a text file, and putting into a hashtable (java)

So basically i have this java project for school, and it requires me to read information from a text file, create an object from that information, and then insert that object into a hash table.
I am able to read the information from the text file and create the object, however, for some reason, my program isnt inserting the object into the hash table. Because then at the end of the program, when i try to search for the object (via its ID), it just returns 'not found'.
EDIT: My question is not how to compare strings. It is how to take the information from the text file, turn it into an object, and then insert it into the hash table. I know how to take the info and turn it into an object, but for some reason, it is not being inserted into the hashtable. Nothing i am inserting into the hashtable from the textfile is actually being inserted. That is the problem I am having.
This is the code i have written:
package test;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class testmain {
public static void main(String[] args) throws FileNotFoundException{
StorageSystem ss = new StorageSystem();
ss.createhashtable();
String line;
String [] parts;
File in = new File("database.txt");
Scanner sc = new Scanner(in);
while(sc.hasNextLine()){
line = sc.nextLine();
parts = line.split(" ");
String type = parts[0];
String name = parts[1];
String id = parts[2];
String price = parts[3];
if(type.equals("Insert")){
Product p1 = new Product(name, id, Double.parseDouble(price));
ss.insert(p1);
}
else if(type.equals("remove")){
ss.remove(id);
}
}
sc.close();
System.out.println(ss.searchbyID("123"));
System.out.println(ss.searchbyID("232"));
System.out.println(ss.searchbyID("444"));
System.out.println(ss.searchbyID("456"));
//If i do it manually (below), it works. But thats not the point of this project
/*
Product ex = new Product("joe", "123", 22.33);
ss.insert(ex);
System.out.println(ss.searchbyID("123"));
ss.remove("123");
System.out.println(ss.searchbyID("123"));
*/
//When i do it this way ^^, it inserts the object into the hashtable, it
//searches for the object and finds it. it then removes the object. and
//when it searches again it doesnt find anything. And thats how it
//should work.
}
}
//Below is the class that contains the information on the hashtable
import java.io.*;
import java.util.Scanner;
public class StorageSystem {
private final static int tablesize = 1000;
private static Product[] table = new Product[tablesize];
public StorageSystem(){
table = new Product[tablesize];
for(int i = 0; i < tablesize; i++){
table[i] = null;
}
}
public void createhashtable(){
table = new Product[tablesize];
for(int i = 0; i < tablesize; i++){
table[i] = null;
}
}
public void useExistingTable(Product[] pt){
table = pt;
}
public String searchbyID(String idnum){
int hash = (Integer.parseInt(idnum) % tablesize);
if(table[hash] == null){
return "Not Found.";
}
else{
Product entry = table[hash];
while((entry != null) && (entry.getID() != idnum)){
entry = entry.getNext();
}
if(entry == null){
return "Not Found.";
}
else
return entry.toString();
}
}
public void insert(Product p){
String n = p.getName();
String i = p.getID();
double pr = p.getPrice();
int hash = (Integer.parseInt(i) % tablesize);
if(table[hash] == null){
table[hash] = new Product(n, i, pr);
}
else{
Product entry = table[hash];
while((entry.getNext() != null) && (entry.getID() != i)){
entry = entry.getNext();
}
entry.setNext(new Product(n, i, pr));
}
}
public void remove(String idnum) {
int hash = (Integer.parseInt(idnum) % tablesize);
if (table[hash] != null) {
Product prevEntry = null;
Product entry = table[hash];
while (entry.getNext() != null && entry.getID() != idnum) {
prevEntry = entry;
entry = entry.getNext();
}
if (entry.getID() == idnum) {
if (prevEntry == null)
table[hash] = entry.getNext();
else
prevEntry.setNext(entry.getNext());
}
}
}
}
If anyone could help me out with this problem that would be great. I have been looking at this thing for a few days now and cant get it to work.
entry.getID() != idnum
change to
!idnum.equals(entry.getID())
in case of entry.getID() returns null
entry.getID() == idnum
is also the same
==================
update:
Since you said the object is not insert into array. I guess it happens here:
There's an id let's say "100" in array. And you want to insert an Product with id "100", so it goes to
else{
Product entry = table[hash];
while((entry.getNext() != null) && (entry.getID() != i)){
entry = entry.getNext();
}
entry.setNext(new Product(n, i, pr));
}
You use entry.getID() != i or !i.equals(entry.getID(), both are ok. But in the end, you set new Product to entry.next.
But in your searchByID() method. You are trying to search through table[hash]
while((entry != null) && (entry.getID() != idnum)){
entry = entry.getNext(); // comment 1
}
if(entry == null){
return "Not Found.";
}
Issue happens here, entry is null will jump out the loop. It means when while loop is done, entry would always be null. You can set a breakpoint on comment 1.

Reduce number of conditional operators

I have an String arraylist. Lets say
private final List<String> fruits = new ArrayList<String>();
Now I have to compare an incoming line against the items in the arraylist
while ((line = in.readLine()) != null) {
if (!(line.equals(fruits.get(0)) || line.contains(fruits.get(1)) ||
line.contains(fruits.get(2)) || line.contains(fruits.get(3)) ||
line.contains(fruits.get(4)) || line.contains(fruits.get(5)) ||
line.contains(fruits.get(6)) || line.equals(fruits.get(7) || line.equals(fruits.get(8)))) {
// "DO SOMETHING"
}
}
I have to match the string exactly for some cases and just use contains for some cases. But at last I should not have more than 3 conditions in my if clause.
Since you want to use either equals() or contains() on each fruit in the list and your fruits are ever growing, consider turning your list into a map, where you store the desired method by fruit.
private enum Method {
CONTAINS,
EQUALS;
}
#Test
public void testFruits() throws IOException {
Map<String, Method> methodByFruit = new HashMap<>();
methodByFruit.put("apple", Method.CONTAINS);
methodByFruit.put("pear", Method.CONTAINS);
methodByFruit.put("grenade apple", Method.CONTAINS);
methodByFruit.put("banana", Method.EQUALS);
methodByFruit.put("kiwi", Method.EQUALS);
BufferedReader in = new BufferedReader(new StringReader("kiwi2"));
String line;
while ((line = in.readLine()) != null) {
boolean success = false;
for (Entry<String, Method> entry : methodByFruit.entrySet()) {
String fruit = entry.getKey();
Method method = entry.getValue();
if (method == Method.EQUALS) {
success = line.equals(fruit);
} else {
success = line.contains(fruit);
}
if (success) {
break;
}
}
if (!success) {
System.out.println("DO SOMETHING");
}
}
}
Your requirement is not clear. Whether the equality check is specifically for index 7 or 8 only or what. But anyway here is my suggestion. you can make a simple method to check if line contains subset from the list
public boolean isFound(List<String> f, String l){
for(int i=0;i<f.size();i++){
if(l.contains(f.get(i)){
return true;
}
}
return false;
}
Then you can check it like this:
if(isFound(fruits, line) || fruits.contains(line)){
//Do Something
}

Compare one ArrayList and combine common elements

I've been working on an algorithm to loop through one ArrayList containing a custom object. I'm now on hour 20 and I've gotten almost nowhere.
ArrayList<TicketItem> all = new ArrayList<>();
// ... 'all' gets filled here ... //
ArrayList<TicketItem> allCopy = new ArrayList<>(all);
for (int i = allCopy.size() - 1; i > 0; i--) {
TicketItem last = allCopy.get(i);
for (int j = 0; j < all.size(); j++) {
TicketItem compare = all.get(j);
if (last.getInt(TicketItem.TICKET_ITEM_ID) != compare.getInt(TicketItem.TICKET_ITEM_ID)) {
if (last.canBeGrouped(compare)) {
last.put(TicketItem.TICKET_ITEM_NUMBER, compare.getInteger(TicketItem.TICKET_ITEM_NUMBER));
allCopy.set(i, last);
break;
}
}
}
}
This works when it wants to and to be honest, it's probably really ugly. I just can't get my head around a better option.
The important method inside TicketItem is this one:
public boolean canBeGrouped(TicketItem other) {
if (other == null)
return false;
if (getBoolean(TicketItem.TICKET_ITEM_VOID))
return false;
if (other.getBoolean(TicketItem.TICKET_ITEM_VOID))
return false;
if (getInteger(TicketItem.MENU_ITEM) == null)
return false;
if (getInteger(TicketItem.MENU_ITEM).equals(other.getInteger(TicketItem.MENU_ITEM))
&& getBigDecimal(TicketItem.TICKET_ITEM_TOTAL).compareTo(
other.getBigDecimal(TicketItem.TICKET_ITEM_TOTAL)) == 0) {
ArrayList<TicketItemModifier> mThis = getModifiers();
ArrayList<TicketItemModifier> mOther = other.getModifiers();
if (mThis == null && mOther == null)
return true;
if (mThis != null && mOther != null) {
if (mThis.size() == mOther.size()) {
for (int i = 0; i < mThis.size(); i++) {
TicketItemModifier m1 = mThis.get(i);
TicketItemModifier m2 = mOther.get(i);
Integer m1MenuModifierId = m1.getInteger(TicketItemModifier.MENU_MODIFIER_ID);
Integer m2MenuModifierId = m2.getInteger(TicketItemModifier.MENU_MODIFIER_ID);
if (!(m1MenuModifierId != null && m2MenuModifierId != null && m1MenuModifierId
.equals(m2MenuModifierId))) {
return false;
}
}
return true;
}
}
}
return false;
}
Again, super ugly especially the for loop in there that works when it wants to. If need be I can modify hashCode and equals methods for both classes TicketItem and TicketItemModifier, however I would like to stay away from those two methods and do something along the lines of Comparable classes because just because they can be grouped does not mean they are equal.
What I want to do basically is go through one ArrayList filled with TicketItem objects and when two can be grouped I need to change the TicketItem object to match it.
I would suggest you create a new property or function like TickeItemCode which should be string concatenation of MENU_ITEM+ "-"+ TICKET_ITEM_TOTAL+ "-" + MENU_MODIFIER_IDs in modifiers list. you can filter the list to remove items where TICKET_ITEM_VOID is true and then sort by new property TickeItemCode and do grouping. This way you can reduce your time from n^2 to nlogn

Performing if else check and setting value for later use. Looking for a cleaner way

I'm writing a customer parser to extract key parts of information from application logs to help with debugging issues.
Within the parser code I have lots of examples of the following sort of logic:
String element = "";
if( rawLogText.contains("RequestType") ) {
element = "RequestType";
}
else if( rawLogText.contains("ResponseType") ) {
element = "ResponseType";
}
if( element.equals("") ) {
return;
}
I feel like there is a cleaner way to do this sort of check-set-return-process logic.
Can anyone come up with a cleaner way to do it?
Here's what I had initially, but the multiple contains checks bugged me.
String element = "";
if( rawLogText.contains("RequestType") || rawLogText.contains("ResponseType") ) {
if( rawLogText.contains("RequestType") ) {
element = "RequestType";
}
else if( rawLogText.contains("ResponseType") ) {
element = "ResponseType";
}
}
else {
return;
}
I'd also like to avoid ternary statements.
What about this instead of checking element.equals():
String element = null;
if( rawLogText.contains("RequestType") ) {
element = "RequestType";
}
else if( rawLogText.contains("ResponseType") ) {
element = "ResponseType";
} else {
return;
}
//continue the process
I think that #GuillermoMerino's solution is probably the best.
But if you repeated this pattern a lot in your parsing you could abstract it as a helper method; e.g.
public String matchWord(String input, String... words) {
for (String word: word) do
if (input.contains(word) ) {
return word;
}
}
return "";
}
and use it like this:
String element = matchWord(rawLogText, "RequestType", "ResponseType");
if (element.equals("")) {
return;
}
Let's say you put all the types in a string array:
String[] possibleTypes = {"RequestType", "ResponseType"}; // and such
Now you can use a for loop to check and set value
String element = ""
for (String type : possibleTypes) {
if (rawLogText.contains(type)) {
element = type;
break;
}
}
if( element.equals("") ) {
return;
}

How do I prevent an IndexOutOfBoundsException error in this Java code?

Basically I just want an error message when there are no more records to show in the array list. What do I need to tweak?
public void nextRecord()
{
if(records.size() > 0)
{
recordCount++;
if(records.get(recordCount) != null)
{
String[] array = records.get(recordCount).split(",");
String item = array[0].trim().replaceAll("\"", "");
String number = array[1].trim();
String cost = array[2].trim();
String amnt = array[3].trim();
txtItem.setText(item);
txtNumber.setText(number);
txtCost.setText(cost);
txtAmount.setText(amnt);
}
}
else if (records.get(recordCount) == null)
{
JOptionPane.showMessa
Check the size of arrayList before calling get() like,
if(recordCount< records.size() && records.get(recordCount) != null)
{
//Do the processing
} else {
JOptionPane.showMessa
}

Categories

Resources