For this assignment, I'm required to save instances of a custom data class (called User) each containing 2 strings into a TreeSet. I must then search the TreeSet I created for a string taken from each line of another file. The first file is a .csv file in which each line contains an email address and a name, the .txt file contains only addresses. I have to search for every line in the .txt file, and I also have to repeat the entire operation 4000 times.
I can't use .contains to search the TreeSet because I can't search by User, since the .txt file only contains one of the two pieces of information that User does. According to information I've found in various places, I can take the iterator from my TreeSet and use that to retrieve each User in it, and then get the User's username and compare that directly to the string from the second file. I wrote my code exactly as every site I found suggested, but my program still gets stuck at an infinite loop. Here's the search code I have so far:
for (int i = 0; i < 4000; i++)//repeats search operation 4000 times
{
try
{
BufferedReader fromPasswords = new BufferedReader(new FileReader("passwordInput.txt"));
while ((line = fromPasswords.readLine()) != null)
{
Iterator it = a.iterator();
while (it.hasNext())
{
//the infinite loop happens about here, if I put a println statement here it prints over and over
if(it.next().userName.compareTo(line) == 0)
matches++; //this is an int that is supposed to go up by 1 every time a match is found
}
}
}
catch (Exception e)
{
System.out.println("Error while searching TreeSet: " + e);
System.exit(0);
}
}
For some additional info, here's my User class.
class User implements Comparable<User>
{
String userName;
String password;
public User() { userName = "none"; password = "none"; }
public User(String un, String ps) { userName = un; password = ps; }
public int compareTo(User u)
{
return userName.compareToIgnoreCase(u.userName);
}
} //User
I've done everything seemingly correctly but it looks to me like iterator doesn't move its pointer even when I call next(). Does anyone see something I'm missing?
Edit: Thanks to KevinO for pointing this out- a is the name of the TreeSet.
Edit: Here's the declaration of TreeSet.
TreeSet<User> a = new TreeSet<User>();
Are you certain there's an infinite loop? You're opening a file 4000 times and iterating through a collection for every line in the file. Depending on size of the file and the collection this could take a very long time.
Some other things to be aware of:
Later versions of Java have a more succinct way of opening a file and iterating through all the lines: Files.lines
You don't need an Iterator to iterate through a collection. A normal for-each loop will do or convert it to a stream
If all you want to do is count the matches then a stream is just as good
Putting all that together:
Path path = Paths.get("passwordInput.txt");
Set<User> users = new TreeSet<>();
long matches = Paths.lines(path)
.mapToLong(l -> users.stream()
.map(User::getName).filter(l::equals).count())
.sum();
Related
My task is:
The program should read items from the user. When all the items from the user have been read, the program prints the information of each item.
For each item, its identifier and name should be read. If the identifier or name is empty, the program stops asking for input, and prints all the item information. Modify the program so that after entering the items, each item is printed at most once. Two items should be considered the same if their identifiers are the same (there can be variation in their names in different countries, for instance).
If the user enters the same item multiple times, the print uses the item that was added first.
I have done the code below, but it will still add items with the same identifier.
Why? How can I fix it?
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ArrayList<String> itemsName = new ArrayList();
ArrayList<String> itemsIdentifier = new ArrayList();
while(true){
System.out.println("Identifier? (emppty will stop)");
String identifier = scanner.nextLine();
if( identifier.isEmpty()){
break;
}
System.out.println("Name? (emppty will stop");
String name = scanner.nextLine();
if(name.isEmpty()){
break;
}
if(!(itemsIdentifier.contains(identifier))){
itemsIdentifier.add(identifier);
}
if(!(itemsName.contains(name))){
itemsName.add(name);
}
}
System.out.println("==Items==");
int j = 0;
for(String i: itemsIdentifier){
System.out.println(i + ": "+ itemsName.get(j));
j++;
}
}
}
I think the problem with your code is that you are adding name into itemsName list even when you are not adding identifier into itemsIdentifier list in the following lines
if(!(itemsIdentifier.contains(identifier))){
itemsIdentifier.add(identifier);
}
if(!(itemsName.contains(name))){
itemsName.add(name);
}
Ideally shouldn't you either add both name and identifier or don't add any?
You have a while(true) loop which will keep going indefinitely, you are breaking the loop only if the user input is empty, but you are not doing anything when the lists already contain an identifier more than once. (You are adding to the list if you have a unique identifier).
EDIT
I have other misgivings on the code above (I am assuming that this is an assignment), but since I do not know if you built this on top of what the lecturer gave you, I can only speculate. Some thoughts on the code above:
You could create your own Record object.
As per the instructions, you would need to override the equals method.
Instead of having two lists, you would have only 1 list of type Record.
Try something like this:
if(!(itemsIdentifier.contains(identifier))) {
itemsIdentifier.add(identifier);
itemsName.add(name);
}
In your code, if the id is already in the list, the name could still be added...
You only need to check whether the identifier is similar or not. The name similarity condition is not required. As long as the identifiers are different( though they have the same name), you still add them to the itemsIdentifier and itemsName. On the other hand, if the identifiers are identical, there is no need to run a check on the similarity of the name.
I have an array list in a class the array is called realtorList. From my main class I store objects with realtor data to the realtorList.
My data that is stored to a text file and is read in the first line.
This is the first element in the realtorList after I store the first line of data.
[Realtor{licenseNumber=AA1111111, firstName=Anna, lastName=Astrid, phoneNumber=111-111-1111,
commission=0.011}]
When I read the next line of data from the input file I need to see if the licenseNumber in bold already exists in the realtorList. I am having trouble figuring out how to go about doing this.
For example if the next realtor data license number is AA1111111 how do I check the realtorList for AA1111111 which does exist for this example.
A really simple way to do this would be to have a String ArrayList running alongside (for example, one called licenses) and use an if statement with indexOf to return if that license value is already in the List. Since the licenses ArrayList only has one value it can be easily searched with indexOf.
An example would be
private boolean checkLicense (String licenseNumber) {
int i = licenses.indexOf(licenseNumber);
if(i == -1) {
return false;
} else {
return true;
}
}
Similar code works in one of my projects where a dynamic List of motors for a robot checks to see if there's already a motor with the listed port before adding a new one.
Another method could use a for loop for a linear search such as
private boolean checkLicense (String licenseNumber) {
for(int i = 0; i < (realtorList.size() - 1); i++) {
if (licenseNumber.equals(realtorList[i].getLicenseNumber())) {
return true;
}
}
return false;
}
This would perform a linear search of each and every object until it finds it (it would need to be in a method like the one for the example above to work this way)
I have an ArrayList containing fields like FirstName,LastName,Employee ID,Employee Job etc., When used to display the list, if at all an employee has the same first and Last names, his name(first+Last Name) should be appended with the employee ID and Job seperated by hyphen.
Iam able to detect the duplicates using Hash Set using Add Operation of Set. But on appending ID , it is being done for only one of the duplicates.
I should be able to differentiate Employees with same names based on appended ID and job.
Thanks :-)
Here is my Code:
List<AgentInfoVO>agentQueuesInfo = new ArrayList<AgentInfoVO>();
List<AgentInfo>agentQueues = null;
Set<AgentInfo>uniqueSet = null;
StringBuffer lastName = null;
if(agentQueuesInfo != null){
agentQueues = new ArrayList<AgentInfo>();
uniqueSet = new HashSet<AgentInfo>();
forEach(AgentInfoVO agentInfoVO : agentQueuesInfo){
AgentInfo agentInfo = new AgentInfo();
agentInfo.setFirstName(agentInfoVO.getFirstName());
agentInfo.setLastName(agentInfoVO.getLastName());
// to check if duplicate names exist and append ID and Job to duplicates
if(!uniqueSet.add(agentInfo)){
lastName = agentInfoVO.getLastName();
if(agentInfoVO.getAgentEmpID() != null){
lastName = lastName.append("-" +agentInfoVO.getAgentEmpID());
}
if(agentInfoVO.etEmpJob() != null){
lastName = lastName.append("-" +agentInfoVO.etEmpJob());
}
agentInfo.setLastName(lastName.toString());
}
agentInfo.setAgentEmpID(agentInfoVO.getAgentEmpID());
agentInfo.setEmpJob(agentInfoVO.etEmpJob());
agentQueues.add(agentInfo);
}
}
Sorry i dont have a computer close to me, so i cant give you code, but can share my ideas.
Detecting duplicates using set add method will only see the second entry as duplicate, thats why you will miss the first one.
If you can be able to modify AgentInfoVO, and firstName and LastName should uniquely identify AgentInfoVO.
A better way can be to overwrite the hash and equals method using using both fields.
And use this method to detect more than one occurrence and do your appends.
https://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#frequency(java.util.Collection, java.lang.Object).
something like:
Set<AgentInfoVO> duplicateAgentInfoVOs = new HashSet<AgentInfoVO>();
for(AgentInfoVO agentInfoVO : agentQueuesInfo) {
if(Collections.frequency(agentQueuesInfo, agentInfoVO) > 1) {
duplicateAgentInfoVOs.add(agentInfoVO);
}
}
for(AgentInfoVO agentInfoVO : agentQueuesInfo) {
if(duplicateAgentInfoVOs.contains(agentInfoVO)) {
//you can do your appends to agentInfoVO
}
}
Let me clarify the question I am asking. I have a java program I am working on that takes input from the keyboard via a readline library called JLine2. The library takes the entire line types as a command instead on breaking it up into space separated commands and arguments. What I am looking for is a safe way to break up the string that is passed as input.
I have tried using an array but since I am in the early stages of concept I don't yet know how many arguments my largest command will have so using a pre-initialized array I don't think will work. The problem I have ran into is when I check for null values in the array or when I check to see if a particular command or argument is present. Java keeps throwing an exception about the array index being out of scope or something. Because the array does not actually have a value for say array index 1 which is an argument to command in array index 0.
So what I am looking for is a way to take a string and safely split it into parts without having Java yelling at me when and array exception has occurred.
Here is the very slim code I can provide...
ConfigShell.class
package shell;
import java.io.IOException;
import configFS.ConfigFS;
import jline.console.ConsoleReader;
public class ConfigShell {
private ConfigFS config;
public ConfigShell() throws IOException {
config = new ConfigFS();
}
public void init() throws IOException {
ConsoleReader console = new ConsoleReader();
// When the program starts we want to be placed at / (root).
console.setPrompt(">> ");
// In this case an infinite loop is better than a loop based on whether line is equal to null.
// This allows line to be equal to null and still stay inside the shell.
while (true) {
String line = console.readLine();
if (line != null) {
// If pre-initialize the array I can check for null as a value for an array index.
// If I did this at time I needed the array and there were not enough index occupied the system would return an exception.
String[] cmdArgs = new String[4];
// We need to split up the incoming line because JLine2 does not do it for me.
// This allows me to evaluate the entire command piece by piece rather all at once.
cmdArgs = line.split("\\s+");
if (cmdArgs[0] != null && cmdArgs[0].equals("add")) {
if (cmdArgs[1] != null && cmdArgs[1].equals("server")) {
if (cmdArgs[2] != null) {
config.addServer(cmdArgs[2]);
System.out.println("Added server " + cmdArgs[2] + " to the configuration successfully.");
}
}
}
if (cmdArgs[0].equals("exit")) {
System.exit(0);
}
}
}
}
}
Note for testing: My Start.class main method makes a call to the init method in the above file.
You can do:
String cmdArgs = line.split("\\s+");
and then, before accessing any particular index, check the size of the array so that you do not get ArrayIndexOutOfBoundException
Something like this:
if(cmdArgs.length>=2){
//It means you have at least 2 elements
//Now its safe to access cmdArgs[0] and cmdArgs[1]
}
If all your problem is to have a storage for a variable number of strings you can use ArrayList<String> object.
You declare it like ArrayList<String> as = new ArrayList<String>();
Then when you split something from your command string you will simply use add method:
as.add(yourString);
If you need to retrieve a particular element of the ArrayList you can use its get method:
as.get(0);
You can process all elements with for each loop:
for(String str: as) {
println(str):
}
Have a look here for info and here for an example.
As I think you can use StringTokenizer class and its methods for your requirement.
see the sample code below:
if(line!=null)
{
StringTokenizer st=new StringTokenizer(line);// by default it takes space as delimiter....you can use as required as second argument in constructor...
while(st.hasMoreTokens())
{
String token1=st.nextToken();
// do your stuffs here..........
// I don't know exactly about your required logic here......
/* if(token1.equals("add"))
{
String token2=st.nextToken();
if(token2.equals("server"))
{
String token3=st.nextToken();
config.addServer(token3);
System.out.println("Added server " + token3 + " to the configuration successfully.");
}
}
*/
}// while closing...
}// outer if closing...
Or as PM 77-1 told you can use ArrayList. But as my opinion LinkedList should be a better option here.
Current assignment needs me to write a program to read a file with instructions in a very tiny and basic programming language (behaves a little like FORTRAN) and execute those instructions. Basically it's a simple interpreter for the language I guess. It's completely linear, with statements all being defined in sequence and it only has String and integer variables. There are 8 keywords and 4 arithmetic operators I would need to find and define if they exist within the source file, and each line must start off with one of the reserved words.
A program in this language might look something like this:
#COMMENTS
LET.... (declares variables with values)
INTEGER myINT
STRING myString
CALCULATE...
PRINT
PRINTLN
END
Can I use a switch block instead of if-loops to find and then execute all these? My concern is that switches don't work with Strings in Java 6, which is what I'm supposed to be using, but I don't see how to easily assign various int values so the switch block would work. Thanks in advance for any suggestions and advice!
If your language is so simple that every statement begins in its own line and is identified by one word only, then (as Gray pointed out in another comment) you can split the words in each line, then compare the first word against a map. However, I would suggest, instead of mapping the words to ints and then doing one big switch, to map them into objects instead, like this (suggested by Dave Newton):
interface Directive {
public void execute(String line);
}
class LetDirective implements Directive {
public void execute(String line) { ...handle LET directive here... }
}
...define other directives in the same way...
Then define the map:
private Map<String, Directive> directives = new HashMap<String, Directive>();
directives.put("LET", new LetDirective());
...
Then in your parsing method:
int firstSpace = line.indexOf(' ');
String command = line;
if (firstSpace > 0)
command = line.substring(0, firstSpace);
Directive directive = directives.get(command.toUpperCase());
if (directive != null)
directive.execute(line);
else
...show some error...
Each directive would have to parse the rest of the line on its own and handle it correctly inside its execute() method.
The benefit of this over a switch is that you can handle a larger amount of commands without ending up with one gigantic method, but instead with one smaller method per each command.
If you are talking about converting strings to integers then you could do it with an Java enumerated type:
private enum ReservedWord {
LET,
...
}
// skip blank lines and comments
String[] tokens = codeLine.split(" ");
ReservedWord keyword;
try {
keyword = ReservedWord.valueOf(tokens[0]);
} catch (IllegalArgumentException e) {
// spit out nice syntax error message
}
You could also put the processing of the line inside of the enum as a method if you'd like. You could also do it with a Map:
private final Map<String, Integer> reservedWords = new HashMap<String, Integer>();
private final int RESERVED_WORD_LET 1
...
{
reservedWords.put("LET", RESERVED_WORD_LET);
...
}
// skip blank lines and comments
String[] tokens = codeLine.split(" ");
Integer value = reservedWords.get(tokens[0]);
if (value == null) // handle error... ;
switch (value) {
case 1:
// LET
...
}