import java.util.*;
class Student{
final String name;
final String gender;
public String number;
static Map<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
static ArrayList<String> nameandNumber = new ArrayList<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender,nameandNumber);
}
void getPersonByGender() {
String[] Liste = hm.get("Man").toArray(new String[0]);
for (int i = 0; i < Liste.length - 1; i += 2) {
System.out.println(Liste[i] + "\t<------>\t" + Liste[i + 1]);
}
}
}
hello guys i am creating a class and this class will return me 10 student information which I will give (according to the difference between men and women). When i try to use getPersonByGender's function this function gives me all students.
static ArrayList<String> nameandNumber = new ArrayList<>();
new is useful: Count the amount of times your code ever invokes new ArrayList. It's a fairly easy answer: Once. For your entire program.
If you only call new once, that means there is only one list. In the whole system.
No wonder then: This:
nameandNumber.add(this.number);
is called 10 times (because 10 students) for a single 'run' of your app. Thus, that one list you have must therefore have all these numbers added together - that's why you see all the data.
Your code is, unfortunately, layers of bad design decisions.
You can 'fix' the problem (but it'll still be fragile code that is hard to read), or you can 'fix' the design (which is more work).
Fix the problem
Instead of 1 list shared by all students which obviously can't work, you want to call new ArrayList for each student. Get rid of that static single arraylist, and instead make one every time:
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
var nameandNumber = new ArrayList<String>();
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender, nameandNumber);
}
Now you call new ArrayList the right number of times throughout one run of your program, for example.
But you're still in trouble here - because you decided to use a List to represent a single idea (a student), you have confused yourself: A given gender maps to multiple students. Given that a single student is represented by a List<String>, multiple students would be a List<List<String>> and, oof, this is getting real complex, real fast.
We could plug away at fixing this further but let's take a step back and fix your design instead!
Fix the design
More generally, java's typing system is highly nominal: Types have names, and the more descriptive the name, the better.
Student is a far better, clearer name than List<String>. How is a reader of your code supposed to know that those List<String> objects specifically are intended to contain precisely 2 strings, the first of which is the student's name, the second of which is their student ID number? It doesn't say that anywhere. If you mess it up you get no compiler errors of any kind.
You have a type, right there, that properly describes that concept: Student!
So why not replace this bad code:
static Map<String, ArrayList<List<String>>> hm = new HashMap<String, ArrayList<List<String>>>();
With this much improved code:
static Map<String, List<Student>> genderMap = new HashMap<>();
It has all sorts of improvements:
It has a proper name. hm doesn't mean anything.
It uses <> to be shorter - you don't need to repeat that stuff.
It codes to the principle (List) instead of the concrete class.
It uses nominal types - this maps a gender string to a list of students. And the code reads the same way, that's good.
Putting it together:
class Student {
final String name;
final String gender;
final String number;
static Map<String, List<Student>> genderMap = new HashMap<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
List<Student> studentsForThisGender = genderMap.get(gender);
if (studentsForThisGender == null) {
// There is no list yet; we need to make one.
genderMap.put(gender, studentsForThisGender = new ArrayList<>());
}
studentsForThisGender.add(this);
}
static void getPersonByGender() {
Student[] liste = genderMap.get("Man").toArray(new Student[0]);
for (Student student : liste) {
System.out.println(student.name + "\t<------>\t" + student.number);
}
}
}
Note:
getPersonByGender is now static - that's a thing you do to the concept of 'students' not any particula student.
Instead of this nebulous 'print list[0] - you just sorta have to know that's the name', we print student.name which documents itself.
We fixed the problem where you were list-confused.
If you get a little further along your java studies, that 'get the list of students for a given gender, and make a new list if neccessary' can be put more succintly. The last 4 lines can be reduced to:
genderMap.computeIfAbsent(gender, () -> new ArrayList<>()).add(this);
But I bet that syntax, and what is happening there, hasn't been covered yet in your java course.
Related
Pretty simply, we have to implement a sorting function of our own choice to sort through a database of customers. The customers are imported from an excel file and stored in an array. I have chosen Mergesort to make a different question involving big O notation a slam dunk.
Here's the import and the creation of the array
Scanner sc = new Scanner(new File("customers.csv")); Customer[] customers = new Customer[1000];
The customer class looks like this
`
class Customer implements Comparable{
private int cusNo;
private String dateOfBirth;
private String firstName;
private String lastName;
//The constructor
public Customer(int cusNo, String dateOfBirth, String firstName, String lastName)
{
this.cusNo = cusNo;
this.dateOfBirth = dateOfBirth;
this.firstName = firstName;
this.lastName = lastName;
}
The problem I have as described in the title comes with the comparison section within the merge method of the mergsort algo.
Customer[] tempCus = new Customer[1000];
int c = 0;
while(i < mid && j < UpperB) {
if(customers.getFirstName[i].compareTo.(customers.getFirstName[b])<=0) {
tempCus[i] = Customer[i];
I honestly have no idea how to use the compareTo method in this situation given the construction of the class and would really appreciate a solution/ a context specific explanation or if I've gone down a bad path a bit of redirection. I have gone with what I've gone with as a bit of a shot in the dark and after multiple different attempts to get the syntax for compareTo correct. Quite confident compareTo is the correct choice, but the implementation is beyond me. Am generally unsure of how to call back a specific value in an array without the extra difficulty of the method and the bracket forrest that comes with it.
Using the in built sort() method is not an option given the task
Don't do it like that but either have Customer implement Comparable<Customer> and implement the method compareTo(Customer other) or use a Comparator<Customer> and implement the method compare(Customer left, Customer right). The implementation would be basically the same with the objects being this and other or left and right. Then compare the relevant elements one after another until you've compared all or the result is != 0.
A simple way to get a comparator would be something like this:
Comparator<Customer> comparator = Comparator.comparing( Customer:: getFirstName).thenComparing( Customer::getLastName );
Then use it like this (reusing your code so I didn't check that for correctness):
//assuming i and b are correct indices
if(comparator.compare(tempCus[i], tempCus[b])<=0) {
...
Since you want to use Comparable here's an example (think about it and expand as needed):
class Customer implements Comparable<Customer> {
public int compareTo(Customer other) {
//or: int result = this.firstName.compareTo(other.getFirstName());
int result = String.compare(this.firstName, other.getFirstName());
//0 means the strings are equal, otherwise we're done
if( result != 0 ) {
return result;
}
result = String.compare(this.lastName, other.getLastName());
//if needed add more comparisons here
return result;
}
}
Then just use it like if(tempCus[i].compareTo(tempCus[b]) <= 0)
So I am reading from a file with scanner it has the similar format:
title, name, age
Mr, Matthew, 20
mr, Paul, 30
miss, Anne, 24
CSV^
class person{
String name, title;
int age;
public crimeData(String csv){
String[]list = csv.split(",", -1);
name = list[0];
title = list[1];
age = list[2];
}
}
Console Program
Scanner input = new Scanner(System.in);
System.out.println("Please select what data you want to load:");
String selection = input.next();
int temp = 0;
for(int i=0; i< header.length; i++){
if(header[i].equals(selection)){
temp = i;
break;
}
}
temp will give us the index of the option specified so if it is 2 we will want to access the age property
When my console application runs I prompt them(the user) for the data that they want.
So they may enter "age" So I am lost on how I may take this "age" String and access the person object with it.
The ideal case for the program output should be: 20,30,24 going through each age and printing
I take their input so String input = scanner.nextLine();
Then I loop through my array of person objects to get the index of the input. Once I have this index I then want to access the property of person at the index. So like if my index was 1 I would want to access the property 'name'.
In javascript I could take the string and say person['age'] although java's a whole different story. I have looked into java's "reflection API" although it's a heavy learning curve.
I have looked into java's "reflection API" although it's a heavy learning curve.
Well, Reflection is the way to go. It's widely used in many frameworks.
But perhaps a simpler solution will fit your needs. Use a switch to decide which attribute to return, and encapsulate this in a method of the Person class:
class Person {
private String name, title;
private int age;
public loadData(String csv){
String[] list = csv.split(",");
name = list[0];
title = list[1];
age = Integer.parseInt(list[2]);
}
public Object attribute(String attribute) {
switch (attribute) {
case "name": return this.name;
case "title": return this.title;
case "age": return this.age;
default: throw new RuntimeException("Invalid attribute: " + attribute);
}
}
}
Encapsulating the switch inside the method is in line with OOP principles, since it hides how attributes are stored from other objects, only exposing an interface to query them. Reflection breaks all encapsulation.
Though in general I am not in favor of using Map for holding fields for an object, if the number of properties is large and could even potentially vary across CSV files (e.g., some file has the University a person attended, another does not), then using a Map to hold the properties might be appropriate.
In this case, one would define a simple Person class:
public class Person {
Map<String, String> props = new HashMap<>();
public void addProperty(String propertyName, String value) {
// could add error checking to ensure propertyName not null/emtpy
props.put(propertyName, value);
}
/**
* returns the value of the property; may return null
*/
public String getProperty(String propertyName) {
return props.get(propertyName);
}
}
If it is know that certain attributes/properties will always be loaded, then accessors such as getName() could be added:
public String getName() {
return props.get("name");
}
public int getAge() {
String age = props.get("age");
// or throw exception if missing
return (age != null ? Integer.parseInt(age) : -1);
}
Though note I would expect name to not be a single entry for most datasets, as there typically would be last name, first name, etc. Nonetheless, the pattern for a limited number of commonly expected values is the same. Also, you can adapt so that you could get integer values directly for certain well-known fields.
Then, when you parse the file, you keep the title row that has the attribute definitions. Then for each row that you subsequently read, you create a new Person object, and then add the properties in order.
List<Person> allPersons = new ArrayList<>();
while ( (line = READ_NEXT_LINE) ) {
// NOTE: this is not a safe way to handle CSV files; should really
// use a CSV reader as fields could have embedded commas
attrs[] = line.split(",");
Person p = new Person();
for (int i = 0; i < titleRow.length; ++i) {
p.addProperty(titleRow[i], attrs[i]);
}
allPersons.add(p);
}
You can then get a specific Person by Person myPerson = allPersons.get(index_of_person), and much akin to the way you would have used Javascript, you can do String val = myPerson.getProperty("age").
If you need to search by a given attribute, you can then stream/loop over the allPersons and check of equivalence based upon a given property.
// find all people of a given age
List<Person> peopleAge20 = allPersons.stream()
.filter(p -> p.getAge() == 20)
.collect(Collectors.toList());
System.out.println(peopleAge20);
// summary statics (average age) for all people
IntSummaryStatistics stats =
allPersons.stream().mapToInt(p -> p.getAge()).summaryStatistics();
System.out.printf("Average age: %f\n", stats.getAverage());
Note that this approach does break the idea of a Javabean, but that may or may not be an issue depending upon your requirements.
First thing, we should add a constructor to your Person class.
class Person {
public Person(String name, String title, int age) {
this.name = name;
this.title = title;
this.age = age;
}
}
Now while you read the input you can use a Map as follows. Here after reading each line, we create a Person object and then using that person's age we make an entry in the map with key as age and value as Person.
Map<Integer, Person> mapOfPeople = new HashMap<>();
while (input.hasNextLine()) {
String line[] = input.nextLine().split(",");
Perso person = new Perso(line[1], line[0], Integer.parseInt(line[2].trim()));
mapOfPeople.put(person.getAge(), person);
}
Now to fetch a particular Person by age just do
mapOfPeople.get(20);
I am currently working on a project for school and am really struggling. I am supposed to selection sort a group of Student objects and then display them in selection sort order.
Create an array with the size of 10 and assign student details (Name, BroncoId, age and TotalMarks) to the array. Perform the selection sort to sort the students in descending order based on their total marks.
a. Steps:
i. Create the student list (use Random class in java to generate the age (15-25) and total (0-100))
ii. Print the Student List in a table format
iii. Perform selection sort based on the total marks of the students
The place I am stuck at currently is making the selection sort. I understand how to create the selection sort, but I can't seem to translate it for this implementation.
My selection sort code:
public static Student[] selectionSort(Student[] studentList)
{
for(int i = 0; i <studentList.length-1; i++)
{
int minIndex = studentList[i].getGrades();
int pos = i;
for(int j = i + 1; j < studentList.length-2; j++)
{
if(studentList[j].getGrades() > studentList[minIndex].getGrades())
{
minIndex = studentList[j].getGrades();
pos = j;
}
}
int temp = studentList[pos].getGrades();
studentList[pos] = studentList[i];
int k = studentList[i].getGrades();
k = temp;
}
return studentList;
}
When I run this code, the console returns:
I sought tutoring to hopefully fix this problem, but my tutor gave me a few nonfunctional suggestions. We were both stumped at the end of the session.
My code for printing:
public static void printStudentInfo(Student[] students)
{
System.out.println("Name: AGE: idNumber: Score:");
for(Student student: students)
{
if(student.getName().length() <= 49)
System.out.printf("%-50s %-5d %-10s %-4d\n", student.getName(), student.getAge(), student.getID(), student.getGrades() );
else
{
System.out.printf("%-50s %-5d %-10s %-4d\n", student.getName().substring(0,48), student.getAge(), student.getID(), student.getGrades() );
System.out.println();
int i = 0;
while(i <= student.getName().length())
{
System.out.printf("%-50s", student.getName().substring(49 +48*i, 97+48*i) );
System.out.println();
i++;
}
}
}
}
As more of an issue out of passion, I sought to make an interesting print method. My problem is, also that I don't really know how to parse and format a string of 155 characters for instance. What do I put in this while lop to accomplish this?
I want the program to output one object name line like:
49 characters
49 chars
…
What ever is left
It probably won't ever go past three lines, but hey, who says I can't give an example like that? What do I put in the header of the while loop to accomplish this?
PS:
Here is the Student class if you need it.
public class Student
{
private String name;
private int age;
private String idNumber;
private int gradePoints;
public Student(String name, int age, String idNumber, int gradePoints)
{
this.name = name;
this.age = age;
this.idNumber = idNumber;
this.gradePoints = gradePoints;
}
public void setName(String name)
{
this.name = name;
}
public void setAge(int age)
{
this.age = age;
}
public void setidNumber(String idNumber)
{
this.idNumber = idNumber;
}
public void setPoints(int gradePoints)
{
this.gradePoints = gradePoints;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public String getID()
{
return idNumber;
}
public int getGrades()
{
return gradePoints;
}
Welcome to SO Matthew.
Rather than giving you a solution I thought it might be useful to give you a process for solving the problem yourself.
Good practice in software development is to break your problem down into very small components, make sure each of those work perfectly (through unit testing) and then build your solution from those components.
In line with that practice I suggest you do the following:
list each of the individual steps required to do a selection sort on paper.
Pick the simplest one (e.g. swapping two elements).
Write a unit test that would pass if your swap method worked
run the unit test and verify that it fails
write the simplest code you can to make that test pass
write a new test to cover a more complex scenario that isn't yet supported
keep going until you believe that method works perfectly
move onto the next method
once all the components are working perfectly write the method that calls them all using the same process (i.e. test first then code)
If you follow this process then you will end up with a system that you understand perfectly, works, is maintainable, and that you can refactor. It has another very significant benefit: it means when you come to SO with a question you'll be asking about a specific item that you don't know how to solve rather than a 'why doesn't my code work' question. Specific questions tend to get better and faster responses.
In your case, I would start with methods for swapping items (hint: your code for this doesn't work which you'll discover quickly when you write a unit test) and then move on to finding the smallest item in a sublist. Then a method that uses those two to put the smallest item at the start of a sublist. Finally a method that performs that method for all sublist progressively. Make sure each method is working perfectly, including checking validity of arguments, before you move on to putting them together.
I have Googled this for a couple of days without much luck. I am trying to read a text file and use that information to populate the private fields of an array for a class object. I am new to Java and pretty new to programming in general.
What I've come up with for reading into the array seems really clunky and I feel there must be a better way, but I cannot find a good example for this particular kind of case case.
Creating a bunch of string variables was the only way I could get this to work. Perhaps main is a bad place to do this; perhaps Scanner is a poor choice here?
What better ways are there to implement this situation?
My text file that contains Strings and integers separated by whitespace on lines is similar to this:
Joe 2541 555-1212 345 1542 Type
Bob 8543 555-4488 554 1982 Type
... etc.
Here's my majority of my code thus far which is within main:
Scanner in = new Scanner(new FileReader("accounts.txt")); //filename to import
Accounts [] account = new Accounts [10];
int i = 0;
while(in.hasNext())
{
account[i] = new Accounts();
String name = in.next();
String acct_num = in.next();
String ph_num = in.next();
String ss_num = in.next();
int open_bal = in.nextInt();
String type = in.next();
account[i].setName(name);
account[i].setAcctNum(acct_num);
account[i].setPhoneNum(ph_num);
account[i].setSSNum(ss_num);
account[i].setOpenBal(open_bal);
account[i].setType(type);
i++;
}
class Accounts
{
public Accounts()
{
}
public Accounts(String n, String a_num, String ph_num,
String s_num, int open_bal, String a_type, double close_bal)
{
name = n;
account_number = a_num;
phone_number = ph_num;
ssn = s_num;
open_balance = open_bal;
type = a_type;
close_balance = close_bal;
}
public String getName()
{
return name;
}
public void setName(String field)
{
name = field;
}
public String getAcctNum()
{
return account_number;
}
public void setAcctNum(String field)
{
account_number = field;
}
//And so forth for the rest of the mutators and accessors
//Private fields
private String name;
private String account_number;
private String phone_number;
private String ssn;
private int open_balance;
private String type;
private double close_balance;
}
I believe you need to split each line in order to get the data contained in each line. You can use the split() of the string class which will return a string[]. Then you can go through each index of the string array and pass them to the mutator methods of the account class.
Something like this maybe.
while(in.hasNext())
{
// will take each line in the file and split at the spaces.
String line = in.next();
String[] temp = line.split(" ");
account[i].setName(temp[0]);
account[i].setAcctNum(temp[1]);
account[i].setPhoneNum(temp[2] + "-" + temp[3]);
account[i].setSSNum(temp[4]);
account[i].setOpenBal((int)temp[5]);
account[i].setType(temp[6]);
// will account for blank line between accounts.
in.next();
i++;
}
The phone number gets split into two separate indices so you have to rejoin the phone number by accounting for the first 3 digits being in one index and the last 4 being in the next.
Replacing Accounts[] with Set<Accounts> would be more flexible solution as you can process either 10 lines or 10000 accounts without code changes. In general: Consider Collections vs arrays
Using Scanner seems to be reasonable in this particular case, however take a look at the others ways of processing text (performance vs convenience): Scanner vs. StringTokenizer vs. String.Split
Setting up account parameters with mutators or constructor might not be the best choice: Effective Java: Consider a builder when faced with many constructor parameters
I'm creating a race results program and I need to store all the info from a text file that looks like this:
------1 Jackson Bertoli 11 Jasper 15.29-----------------------------------------------------------------------------------------
As you can see, it contains the place, a first name, a last name, a grade, a school name, and a time.
This information needs to be stored together, so I figured the best way to do this was storing it in an object "runner", and then putting all of those "runner" objects inside an arraylist. The only problem with this is that I can't find how to make a different object name for each object in my while loo (which is going through each line to read the elements from the text file) for each separate runner. Is it even necessary to have a different object name for each object? And if I am allowed to have many separate objects with the same name, how do I distinguish them apart? Here's a bit of the code behind it
public void readfile() {
while (initialresults.hasNext()){
strplace = initialresults.next();
dataplace = Integer.parseInt(strplace);
datafirstname = initialresults.next();
datalastname = initialresults.next();
strgrade = initialresults.next();
datagrade = Integer.parseInt(strgrade);
dataschool = initialresults.next();
strorigtime = initialresults.next();
dataorigtime = Double.parseDouble(strorigtime);
runner newrunner = new runner(datafirstname,datalastname, datagrade, dataschool, dataorigtime);
amountofrunners.add(newrunner);
counter++;
}
}
So you can see I'm reading each element from the text file, and then trying to make a new "runner" object, store those elements from the text file in that object, but then I'm stuck putting the next line's elements in that same object name. How can I create a new object name every time to store those elements in? The only reason I'm using an object is because it seems like the best way to keep data organized for a runner. But if I used an array or a list, wouldn't that get disorganized and difficult to sort through? Thank you for your help in advance.
When your loop runs, even though the runner class is being used multiple times, it creates a new (and different) runner object each time the loop iterates (runs through a cycle).
In the line amountofrunners.add(newrunner);, Java basically copies the value(s) stored in newrunner and stores it/them seperately. Every object is different in this case, even if it has the same variable name when it's created.
Using an ArrayList actually makes it easier to sort through because you can use indexes, foreach loops, and other features to help manage the objects stored in the ArrayList.
I think you were trying to do something like this:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public static class Runner{
int place;
String firstName, lastName, school;
double origTime, grade;
Runner(int place,String firstName,String lastName,double grade,String school,double origTime){
this.place = place;
this.firstName = firstName;
this.lastName = lastName;
this.grade = grade;
this.school = school;
this.origTime = origTime;
}
}
public static List<Runner> readfile(Scanner in) {
List<Runner> runners = new ArrayList<Runner>();
while (in.hasNext()){
runners.add(new Runner(
in.nextInt(), // place
in.next(), in.next(), //first, last name
in.nextDouble(), // grade
in.next(), // school
in.nextDouble() // original time
));
}
return runners;
}
public static void main(String[] args) throws IOException {
List<Runner> runners;
try (Scanner s = new Scanner(new BufferedReader(new FileReader("file.txt")))) {
runners = readfile(s);
}
// Do something with runners
}