-3

I'm trying to implement a GUI employee manager on Jswing where data is stored on a text file that I treat as my database and I created a GUI interface to interact with it.

Upon opening up the program, I'm able to use the remove feature and it works fine. But once I click the search button (table searches for values and only displays the values I searched for), and then when I try to remove something, it throws the error you see below.

This is the error I'm getting once I search for something:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 2 >= 2
    at java.base/java.util.Vector.elementAt(Vector.java:466)
    at java.desktop/javax.swing.table.DefaultTableModel.getValueAt(DefaultTableModel.java:660)
    at java.desktop/javax.swing.JTable.getValueAt(JTable.java:2763)
    at java.desktop/javax.swing.JTable.prepareRenderer(JTable.java:5780)
    at java.desktop/javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2210)
    at java.desktop/javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2112)
    at java.desktop/javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1908)
    at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:842)
    at java.desktop/javax.swing.JComponent.paint(JComponent.java:1119)
    at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5311)
    at java.desktop/javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:246)
    at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1337)
    at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5259)
    at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5069)
    at 

------------------EDIT----------------------

Here is my minimal reproducible code:

import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.util.*;
    import javax.swing.JTable;
    import java.io.IOException;  // Import the IOException class to handle errors
    import java.io.File;  // Import the File class
    import java.io.FileNotFoundException;  // Import this class to handle errors
    import javax.swing.table.DefaultTableModel;
    import java.io.FileWriter;
    import javax.swing.event.*;
    import javax.swing.table.*;
class UserManager extends JFrame implements ActionListener {
  // Initializing class variables
  private JTextField firstNameField, lastNameField, salaryField, textField;
  private JButton addButton, removeButton, sortButton, button;
  private JList<Employee> userList;
  private ArrayList<Employee> users;
  private JTable j;
  private DefaultTableModel model;
  private JTextField searchField;

  /*****************************************
  /*Name: UserManager (constructor)
  /*Method Description: Constructor class that runs once upon creation of the object. Creates the frame of the GUI app. Pulls from the database and displays it on the GUI interface.
  /*Method Inputs/Outputs: Outputs the GUI frame of the app.
  ******************************************/
  public UserManager() {
    setTitle("Employee Manager");
    setSize(400, 400);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    // Initializing and binding action items
    firstNameField = new JTextField(20);
    lastNameField = new JTextField(20);
    salaryField = new JTextField(20);
    searchField = new JTextField(20);
    addButton = new JButton("Add");
    addButton.addActionListener(this);
    removeButton = new JButton("Remove");
    removeButton.addActionListener(this);
    sortButton = new JButton("Sort Salary");
    sortButton.addActionListener(this);

    // Pulling data from text file database upon start up
    ArrayList<ArrayList<String>> databaseData = ReadFile();

    users = new ArrayList<Employee>();

    // Adding existing databaseData to users
    try {
      if (databaseData.size() > 0) {
        for (int i = 0; i < databaseData.size(); i++) {
          Employee user = new Employee(databaseData.get(i).get(0), databaseData.get(i).get(1), Integer.valueOf(databaseData.get(i).get(2)));
          users.add(user);
        }
      }
    }
    catch (NumberFormatException nfe) {
      nfe.printStackTrace();
      JOptionPane.showMessageDialog(this, "Internal System Error", "Error", JOptionPane.ERROR_MESSAGE);
    }

    // Creating the list of users
    userList = new JList<Employee>(users.toArray(new Employee[0]));

    // Setting up the JPanels
    JPanel firstNamePanel = new JPanel();
    firstNamePanel.add(new JLabel("First Name:"));
    firstNamePanel.add(firstNameField);

    JPanel lastNamePanel = new JPanel();
    lastNamePanel.add(new JLabel("Last Name:"));
    lastNamePanel.add(lastNameField);

    JPanel salaryPanel = new JPanel();
    salaryPanel.add(new JLabel("Salary:"));
    salaryPanel.add(salaryField);

    JPanel buttonPanel = new JPanel();
    buttonPanel.add(addButton);
    buttonPanel.add(removeButton);
    buttonPanel.add(sortButton);

    JPanel searchPanel = new JPanel();

    
    // Converting 2D arraylist to normal 2D array for JTable
    String[][] data = databaseData.stream().map(u -> u.toArray(new String[0])).toArray(String[][]::new);

    // Initializing column names of JTable
    String[] columnNames = { "FName", "LName", "Salary" };

    // Initialize the JTable and TableModel
    model = new DefaultTableModel(data, columnNames);
    j = new JTable(model);
    j.setBounds(1000, 1000, 900, 900);

    // adding it to JScrollPane
    JScrollPane table = new JScrollPane(j);

    
    JLabel label = new JLabel("Search: ");
    textField = new JTextField(20);
    button = new JButton("Go");
    searchPanel.add(label);
    searchPanel.add(textField);
    searchPanel.add(button);
    button.addActionListener(this);


    // Creating main panel and adding JPanels to it
    JPanel mainPanel = new JPanel(new GridLayout(6, 2));
    mainPanel.add(firstNamePanel);
    mainPanel.add(lastNamePanel);
    mainPanel.add(salaryPanel);
    mainPanel.add(buttonPanel);
    mainPanel.add(searchPanel);
    mainPanel.add(table);
    add(mainPanel);

  }

  /*****************************************
  /*Method Name: actionPerformed
  /*Method Description: Performs functions depending on what action is called. Adds users to the table and db if add button is clicked and removes users from the table and db if remove button is clicked.
  /*Method Inputs/Outputs: Refreshes the table and outputs the new array with the updates.
  ******************************************/
  public void actionPerformed(ActionEvent e) {
    // "Add" button is clicked
    if (e.getSource() == addButton) {
      // Initializing and setting variables
      String firstName = firstNameField.getText();
      String lastName = lastNameField.getText();
      int salary = 0;

      // Checks to see if salary entered is an integer
      try {
          salary = Integer.parseInt(salaryField.getText());
      } 
      catch (NumberFormatException nfe) {
        JOptionPane.showMessageDialog(this, "Please enter a valid salary", "Error", JOptionPane.ERROR_MESSAGE);
        return;
      }

      // Error check to see if full name and age is entered
      if (!firstName.equals("") && !lastName.equals("")) {
        Employee user = new Employee(firstName, lastName, salary);

        // Add the user to the arraylist
        users.add(user);

        // Creating new array with new information
        String[] newEmployeeArr = {firstName, lastName, String.valueOf(salary)};

        // Add new user to GUI
        model.addRow(newEmployeeArr);

        // Update user list
        updateList(users);

        // Resetting input fields
        firstNameField.setText("");
        lastNameField.setText("");
        salaryField.setText("");
      }
      else {
        JOptionPane.showMessageDialog(this, "Please enter a valid full name", "Error", JOptionPane.ERROR_MESSAGE);
      }

    } 

    // "Remove" button is clicked
    else if (e.getSource() == removeButton) {
      try {
        if(j.getSelectedRow() != -1) {
          // remove selected row from the model
          String value = j.getValueAt(j.getSelectedRow(), 0).toString();
          String value1 = j.getValueAt(j.getSelectedRow(), 1).toString();
          String value2 = j.getValueAt(j.getSelectedRow(), 2).toString();

          System.out.println(j.getSelectedRow() + " " + value + value1 + value2);

          // Accounting for if the table was sorted and looking to see where the removed var is in the model
          model.removeRow(removeUserFromTable(value, value1));

          
          // finds the index of the user to remove
          int selectedIndex = removeUser(value, value1, Integer.valueOf(value2));

          // Error checks to see if valid user
          if (selectedIndex != -1) {
            // Remove the selected employee from the users arraylist
            users.remove(selectedIndex);
            JOptionPane.showMessageDialog(null, "Selected row deleted successfully");
            
            // Update the list
            updateList(users);
    
            // Clear inputs
            firstNameField.setText("");
            lastNameField.setText("");
            salaryField.setText("");
          }
        }
        else {
          JOptionPane.showMessageDialog(this, "Employee not selected.", "Error", JOptionPane.ERROR_MESSAGE);
        }
      }
      catch (NumberFormatException nfe) {
        JOptionPane.showMessageDialog(this, "Select a valid row.", "Error", JOptionPane.ERROR_MESSAGE);
      }
    }

    // "Sort" button is clicked
    else if (e.getSource() == sortButton) {
      BubbleSort();
    }

      // "Go" button is clicked to search
    else if (e.getSource() == button) {
      // Creating a searching tool
      TableRowSorter<TableModel> sorter = new TableRowSorter<>(model);
      j.setRowSorter(sorter);
      String text = textField.getText();
      if (text.length() == 0) {
        sorter.setRowFilter(null);
      } else {
        sorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
      }
    }
  }


  /*****************************************
  /*Method Name: updateList
  /*Method Description: Performs the update depending on what operation was made. Updates all lists inside the class and saves the change to the database.
  /*Method Inputs/Outputs: The new list with the updated list is inputted and an updated database text file is the output.
  ******************************************/
  private void updateList(ArrayList<Employee> u) {
    userList.setListData(u.toArray(new Employee[0]));

    // Log update to console
    System.out.println("Updating Database");

    // Overwriting db.txt file with new information
    try {
      // Making changes to the existed db.txt file
      FileWriter fw = new FileWriter("db.txt", false);

      // Loop through each student and write to the text file
      for (int i = 0; i < u.size(); i++) {
        // Re-writing the database file with the updates list
        fw.write(toString(u.get(i).getFirstName(), u.get(i).getLastName(), u.get(i).getSalary()));
      }
      fw.close();
    }
    catch (IOException io) {
      JOptionPane.showMessageDialog(this, "Internal System Error", "Error", JOptionPane.ERROR_MESSAGE);
      return;
    }
  }

  
  /*****************************************
  /*Method Name: removeUser
  /*Method Description: Searches for the selected user in the users arraylist and finds the index if the user exists
  /*Method Inputs/Outputs: The users arraylist and inputted user information is inputted and then outputs where the user is located in the array.
  ******************************************/
  private int removeUser(String firstName, String lastName, int salary) {
    // Loops through users arraylist
    for (int i = 0; i <  users.size(); i++) {
      // If the user exists in the database, remove them
      if (users.get(i).getFirstName().equals(firstName) && users.get(i).getLastName().equals(lastName) && users.get(i).getSalary() == salary) {
        return i;
      }
    }
    // No user by the name and salary was found
    return -1;
  }

  /*****************************************
  /*Method Name: removeUserFromTable
  /*Method Description: Searches for the selected user in the table model and finds the index if the user exists
  /*Method Inputs/Outputs: The user information is inputted and then outputs where the user is located in the model that controls the GUI table.
  ******************************************/
  private int removeUserFromTable(String firstName, String lastName) {
    int ind = 0;
    for (int i = 0; i < model.getRowCount(); i++){
      if (model.getValueAt(i, 0).toString().equals(firstName) && model.getValueAt(i, 1).toString().equals(lastName)) {
          ind = i;
          break;
      }
    }  
    return ind;
  }


  /*****************************************
  /*Method Name: toString
  /*Method Description: Converts three variables into one long string varaible
  /*Method Inputs/Outputs: The user information is inputted and the string that contains all three variables is outputted.
  ******************************************/
  public String toString(String firstName, String lastName, int salary) {
      return firstName + ", " + lastName + ", " + salary + "\n";
  }

  /*****************************************
  /*Method Name: BubbleSort
  /*Method Description: Performs bubble sort on the employees by salary and then updates the jtable
  /*Method Inputs/Outputs: No inputs or outputs, the JTable is updated
  ******************************************/
  public void BubbleSort() {
    try {
      // Array to hold a copy of the users list
      ArrayList<Employee> tempUsers = new ArrayList<Employee>();
    
      // Adding all the initial users to the new array to perform bubble sort
      for (int i =0; i < users.size(); i++) {
        tempUsers.add(users.get(i));
      }
    
      // Performing bubble sort
      for (int i = 0; i < users.size() - 1; i++) {
        // Looping through indexes
        for (int j = 0; j < users.size() - 1; j++) {
          // Comapare the salaries of each indiviual and see if the previous is bigger than the next
          if (users.get(j).getSalary() > users.get(j+1).getSalary()) {
            // Initializing a temp employee to hold value before switching
            Employee temp = users.get(j);
    
            // Swap the employees
            users.set(j, users.get(j+1));
            users.set(j+1,temp);
          }
        }
      }

      int[] selection = j.getSelectedRows();
      System.out.println(Arrays.toString(j.getSelectedRows()));
       for (int i = 0; i < selection.length; i++) {
         selection[i] = j.convertRowIndexToModel(selection[i]);
         System.out.println(selection[i]);
       }

      // Setting the jtable model to the sorted model
      j.setModel(model);
    }
    catch (NumberFormatException nfe) {
      JOptionPane.showMessageDialog(this, "Not Enough Users", "Error", JOptionPane.ERROR_MESSAGE);
      return;
    }
  }
  

  /*****************************************
  /*Method Name: ReadFile
  /*Method Description: Reads the db textfile and stores the values in a 2d arraylist for manipulation.
  /*Method Inputs/Outputs: The 2d arraylist with all the db information is outputted
  ******************************************/
  public static ArrayList<ArrayList<String>> ReadFile() {
    try {
      // Choose db.txt file to look at
      File myObj = new File("db.txt");

      // Create scanner object
      Scanner myReader = new Scanner(myObj);

      // Create 2d list array to hold all the single list arrays of single information
      ArrayList<ArrayList<String>> combinedArr = new ArrayList<ArrayList<String>>();
    
      // While the file reader is still reading lines in the text
      while (myReader.hasNextLine()) {
        // Read strings of text in txt file
        String data = myReader.nextLine();

        // Get user information into an array
        ArrayList<String> temp = GetInfo(data); 

        // Add the person and their salary to the combined array that holds everyones
        combinedArr.add(temp);
      }

      // Close file once there are no more lines to read
      myReader.close();

      return combinedArr;
    } 
    catch (FileNotFoundException e) {
      System.out.println("An error occurred.");
      e.printStackTrace();
    }

    // Return invalid list string with nothing if error
    ArrayList<ArrayList<String>> Invalid = new ArrayList<ArrayList<String>>();
    return Invalid;
  }

  /*****************************************
  /*Method Name: GetInfo
  /*Method Description: Takes in a string of data and parses the three variables in it which are separated by commas
  /*Method Inputs/Outputs: The data string that needs to be parsed is inputted and arraylist containuing the data from the string is outputted.
  ******************************************/
  public static ArrayList<String> GetInfo(String data) {
    String first = "";
    String last = "";
    String sal = "";
    // System.out.println(data[0])
    for (int i = 0; i < data.length(); i++) {
      if (data.charAt(i) == ',') {
        // Start from 2 indexes after the first occurance of the comma
        for (int j = i+2; j < data.length(); j++) {
          if (data.charAt(j) == ',') {
            // Start from 2 indexes after the occurance of the comma
            for (int n = j+2; n < data.length(); n++) {
              sal += data.charAt(n);
            }
            break;
          }
          last += data.charAt(j);
        }
        break;
      }
      first += data.charAt(i);
    }

    // Initializing package array to send all values
    ArrayList<String> arr = new ArrayList<String>();
    arr.add(first);
    arr.add(last);
    arr.add(sal);
    
    return arr;
  }


  /*****************************************
  /*Method Name: main
  /*Method Description: Runs the GUI frame
  /*Method Inputs/Outputs: Ouputs the GUI 
  ******************************************/
  public static void main(String[] args) {
      UserManager frame = new UserManager();
      frame.setVisible(true);
  }
}


class Employee {
  // Initalizing variables
  private String firstName;
  private String lastName;
  private int salary;

  public Employee(String firstName, String lastName, int salary) {
      this.firstName = firstName;
      this.lastName = lastName;
      this.salary = salary;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public int getSalary() {
    return salary;
  }
}
Soccerball123
  • 741
  • 5
  • 17
  • 1
    This question might help: https://stackoverflow.com/questions/3988788/what-is-a-stack-trace-and-how-can-i-use-it-to-debug-my-application-errors – Old Dog Programmer Jan 31 '23 at 23:45
  • 4
    Consider providing a [mcve] which demonstrates your issue. Are you aware that [`JTable` supports inline sorting](https://docs.oracle.com/javase/tutorial/uiswing/components/table.html#sorting) – MadProgrammer Feb 01 '23 at 00:02
  • 2
    Why are you storing the data in two places? You don't need the ArrayList to hold your Users. The TableModel will hold all your Users. So all you need to do is remove the row from the TableModel. Because the data is sorted you need to convert the "view" row number to the "model" row number before you can delete the row from the model. Read the JTable API for the `convert???` methods that do this for you. – camickr Feb 01 '23 at 01:46
  • @camickr I've been trying to use convert as shown in my edit but I'm unable to make the changes appear on the GUI itself, do you know why? – Soccerball123 Feb 02 '23 at 00:19
  • @MadProgrammer yes I am aware, but I am trying to implement a programmable sort myself to get an understanding for this – Soccerball123 Feb 02 '23 at 00:20
  • @MadProgrammer I added the minimal reproducible example, I just really don't know how to solve this problem and I really want to know how to fix it – Soccerball123 Feb 02 '23 at 02:34
  • This is not a valid [mre] -- it has resource requirements that we have no access to, such as some text file, perhaps db.txt. Without this file, with the correct data and in the correct location, this code throws a `FileNotFoundException`. Please read the [MRE](https://stackoverflow.com/help/minimal-reproducible-example) link and test your mre in isolation to be sure that it complies with requirements. Best to do this before asking a question, but better late than never. – Hovercraft Full Of Eels Feb 02 '23 at 02:47
  • 2
    And also, as noted by @OldDogProgrammer, have you used your debugger to step through the code near where the errors are occurring to see for yourself why they are occurring? – Hovercraft Full Of Eels Feb 02 '23 at 02:49
  • 2
    Also: 1) Get most of that code out of the static world and into the instance world. 2) Learn and follow Java naming conventions: Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others. 3) Less cluttering of code with comments. Some commenting is good, but too many frivolous comments makes your code harder to read and understand, something that you don't want to do when asking others for help... – Hovercraft Full Of Eels Feb 02 '23 at 02:59
  • 2
    4) Refacter your code, especially the large `UserManager.java` "God"-class, so that one class has one single responsibility. Best to have multiple small classes that are easy to understand, and that are ***easy to debug and test in isolation***. 5) And then do just that: test each class in isolation to find and fix your bugs. – Hovercraft Full Of Eels Feb 02 '23 at 03:00
  • Also, 6) [edit] and fix your question. The question is asking about an ArrayIndexOutOfBoundsException, but as far as I understand things, the current code doesn't throw this exception. Or does it? – Hovercraft Full Of Eels Feb 02 '23 at 03:03
  • 1
    Side issues: 1) Never set the bounds of a JTable. Never. That will mess with its ability to expand its rows and work correctly within its scrollpane. You really shouldn't set the bounds of any Swing component, but especially JTables and text components such as JTextAreas and JTextFields. 2) GridLayout is not a good layout for the main JPanel. Better might be BoxLayout, or a combination of layouts by nexting JPanels, each using its own simple layout, or consider using GridBagLayout, but again, not GridLayout. – Hovercraft Full Of Eels Feb 02 '23 at 03:14
  • 1
    3) Don't set the size of the JFrame. Instead, first add all GUI components to it, then call `.pack()` on it, and *then* call `.setVisible(true)` on it. – Hovercraft Full Of Eels Feb 02 '23 at 03:14
  • 1
    Forget my suggestion to use a "convert???" method. That advice only applies when you use the JTable built in sorting. Since you are recreating the model in sorted order the view and model rows should be the same. The question is why is your index value of 2, if you only have 2 rows of data when you try to remove a row of data. That is what the Exception is telling you. Note, even if you use the code from the suggested answer below you still need to have the correct index to use when updating the model. – camickr Feb 02 '23 at 03:39
  • In editing the question, you removed an important part of the stack trace. The part of the stack trace that was removed from the question had this line: `at UserManager.actionPerformed(UserManager.java:200)`. It appears that `UserManager` is from your code. The error is associated with line 200 in that source code. – Old Dog Programmer Feb 03 '23 at 16:28

1 Answers1

2

Instead of trying to treat your data and model as two seperate things, you should be wrapping your model around your data and allow to manage it, for example...

public class EmployeeTableModel extends AbstractTableModel {

    private List<Employee> employees;
    private String[] columnNames = {"FName", "LName", "Salary"};
    private Class[] columnClasses = {String.class, String.class, Integer.class};

    public EmployeeTableModel(List<Employee> employees) {
        this.employees = employees;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public String[] getColumnNames() {
        return columnNames;
    }

    public Class[] getColumnClasses() {
        return columnClasses;
    }

    @Override
    public int getRowCount() {
        return getEmployees().size();
    }

    @Override
    public int getColumnCount() {
        return getColumnNames().length;
    }

    @Override
    public String getColumnName(int column) {
        return getColumnNames()[column];
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return getColumnClasses()[columnIndex];
    }

    public void sortBySalary() {
        BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                // This is decending...
                return o2.getSalary() - o1.getSalary();
                // This is acending
                //return o1.getSalary() - o2.getSalary();
            }
        });
        // You could use
        //fireTableRowsUpdated(0, getRowCount() - 1);
        // But this will work just fine and will force a complete 
        // redraw of the table
        fireTableDataChanged();
    }

    public void sortByFirstName() {
        BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getFirstName().compareTo(o2.getFirstName());
            }
        });
        fireTableDataChanged();
    }

    public void sortByLastName() {
        BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getLastName().compareTo(o2.getLastName());
            }
        });
        fireTableDataChanged();
    }

    public void add(Employee employee) {
        int rowCount = getRowCount();
        getEmployees().add(employee);
        fireTableRowsInserted(rowCount, rowCount);
    }

    public void delete(Employee employee) {
        List<Employee> employees = getEmployees();
        for (int index = 0; index < employees.size(); index++) {
            if (employees.get(index).equals(employee)) {
                employees.remove(index);
                fireTableRowsDeleted(index, index);
                break;
            }
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Employee employee = getEmployees().get(rowIndex);
        switch (columnIndex) {
            case 0:
                return employee.getFirstName();
            case 1:
                return employee.getLastName();
            case 2:
                return employee.getSalary();
        }
        throw new ArrayIndexOutOfBoundsException(rowIndex + "x" + columnIndex + " is out of bounds");
    }
}

This is a pretty basic concept of a custom TableModel. It takes in a List of Employees and provides the core implementation of the TableModel but also provides some helper methods, like add and delete which will trigger the appropriate events to allow a JTable to update itself.

You could easily back this with a "database manager" instead, so that sort/delete/add/update actions would all be delegated to it and those updates could be made to the "database" values as well, but I'm going to leave that up to you to figure out.

Now, I separated your BubbleSort into it's own, self contained and re-usable class...

public class BubbleSort {
    public static <T> void sort(List<T> list, Comparator<? super T> comparator) {
        // Performing bubble sort
        for (int i = 0; i < list.size() - 1; i++) {
            // Looping through indexes
            for (int j = 0; j < list.size() - 1; j++) {
                // Comapare the salaries of each indiviual and see if the previous is bigger than the next
                if (comparator.compare(list.get(j), list.get(j + 1)) > 0) {
                    // Initializing a temp employee to hold value before switching
                    T temp = list.get(j);

                    // Swap the employees
                    list.set(j, list.get(j + 1));
                    list.set(j + 1, temp);
                }
            }
        }
    }
}

The only constraint is that you need to pass in a Comparator which is used to compare the two values in order to determine if they should be swapped. "But why do this?" I hear you ask, because it's self contained and re-usable.

So, you want to sort by the salary right? Do you want to sort in ascending...

BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
    @Override
    public int compare(Employee o1, Employee o2) {
        return o1.getSalary() - o2.getSalary();
    }
});

or decending order?

BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
    @Override
    public int compare(Employee o1, Employee o2) {
        return o2.getSalary() - o1.getSalary();
    }
});

Surprisingly, you can now do both, without much of an effort. But wait, what if you want to sort by the first name?!

BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
    @Override
    public int compare(Employee o1, Employee o2) {
        return o1.getFirstName().compareTo(o2.getFirstName());
    }
});

or last name?!

BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
    @Override
    public int compare(Employee o1, Employee o2) {
        return o1.getLastName().compareTo(o2.getLastName());
    }
});

See, re-usable.

You could even come with some combination of those, first name and salary, sure, doable, just write a new Comparator. Want to sort some other type of value, sure, so long as you supply a compatible Comparator, not a problem.

Runnable example

I've not bothered with your "database", as it's really not part of the problem, instead, I've focused on getting the table model and sorting to work together (and deleting, because you know, why not).

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.*;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import javax.swing.border.EmptyBorder;
import javax.swing.table.*;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new MainPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MainPane extends JPanel {

        private EmployeeTableModel employeeTableModel;
        private JTable table;

        public MainPane() {
            Random rnd = new Random();
            List<Employee> employees = new ArrayList<>(
                    Arrays.asList(
                            new Employee[]{
                                new Employee("Alesha", "Golden", rnd.nextInt(999) + 1),
                                new Employee("Gerald", "Guerrero", rnd.nextInt(999) + 1),
                                new Employee("Georgina", "Delacruz", rnd.nextInt(999) + 1),
                                new Employee("Michael", "Delgado", rnd.nextInt(999) + 1),
                                new Employee("Aysha", "Zimmerman", rnd.nextInt(999) + 1),
                                new Employee("Yahya", "Moreno", rnd.nextInt(999) + 1),
                                new Employee("Max", "Reyes", rnd.nextInt(999) + 1),
                                new Employee("Julia", "Salinas", rnd.nextInt(999) + 1),
                                new Employee("Aleeza", "Flores", rnd.nextInt(999) + 1),
                                new Employee("Milton", "Frye", rnd.nextInt(999) + 1),}
                    )
            );
            employeeTableModel = new EmployeeTableModel(employees);
            table = new JTable(employeeTableModel);

            setLayout(new BorderLayout());
            add(new JScrollPane(table));

            JButton sortBySalaryButton = new JButton("Sort by Salary");
            sortBySalaryButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    employeeTableModel.sortBySalary();
                }
            });
            JButton sortByFirstNameButton = new JButton("Sort by First name");
            sortByFirstNameButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    employeeTableModel.sortByFirstName();
                }
            });
            JButton sortLastNameButton = new JButton("Sort by Last name");
            sortLastNameButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    employeeTableModel.sortByLastName();
                }
            });
            // I'd normally use a SelectionListener to monitor
            // changes to the table in order to enable/disable this
            // button, but that's beyond the scope
            JButton deleteButton = new JButton("Delete");
            deleteButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int selectedRow = table.getSelectedRow();
                    if (selectedRow < 0) {
                        return;
                    }
                    Employee employee = employeeTableModel.getEmployeeAt(selectedRow);
                    employeeTableModel.delete(employee);
                }
            });

            JPanel actionPane = new JPanel(new GridBagLayout());
            actionPane.setBorder(new EmptyBorder(8, 8, 8, 8));
            actionPane.add(sortBySalaryButton);
            actionPane.add(sortByFirstNameButton);
            actionPane.add(sortLastNameButton);
            actionPane.add(deleteButton);
            add(actionPane, BorderLayout.SOUTH);
        }

    }

    class Employee {
        // Initalizing variables
        private String firstName;
        private String lastName;
        private int salary;

        public Employee(String firstName, String lastName, int salary) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.salary = salary;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public int getSalary() {
            return salary;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 59 * hash + Objects.hashCode(this.firstName);
            hash = 59 * hash + Objects.hashCode(this.lastName);
            hash = 59 * hash + this.salary;
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Employee)) {
                return false;
            }
            Employee other = (Employee) obj;
            return getFirstName().equals(other.getFirstName())
                    && getLastName().equals(other.getLastName())
                    && getSalary() == other.getSalary();
        }
    }

    public class EmployeeTableModel extends AbstractTableModel {

        private List<Employee> employees;
        private String[] columnNames = {"FName", "LName", "Salary"};
        private Class[] columnClasses = {String.class, String.class, Integer.class};

        public EmployeeTableModel(List<Employee> employees) {
            this.employees = employees;
        }

        public List<Employee> getEmployees() {
            return employees;
        }

        public String[] getColumnNames() {
            return columnNames;
        }

        public Class[] getColumnClasses() {
            return columnClasses;
        }

        public Employee getEmployeeAt(int row) {
            return getEmployees().get(row);
        }

        @Override
        public int getRowCount() {
            return getEmployees().size();
        }

        @Override
        public int getColumnCount() {
            return getColumnNames().length;
        }

        @Override
        public String getColumnName(int column) {
            return getColumnNames()[column];
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return getColumnClasses()[columnIndex];
        }

        public void sortBySalary() {
            BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    // This is decending...
                    return o2.getSalary() - o1.getSalary();
                    // This is acending
                    //return o1.getSalary() - o2.getSalary();
                }
            });
            // You could use
            //fireTableRowsUpdated(0, getRowCount() - 1);
            // But this will work just fine and will force a complete 
            // redraw of the table
            fireTableDataChanged();
        }

        public void sortByFirstName() {
            BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    return o1.getFirstName().compareTo(o2.getFirstName());
                }
            });
            fireTableDataChanged();
        }

        public void sortByLastName() {
            BubbleSort.sort(getEmployees(), new Comparator<Employee>() {
                @Override
                public int compare(Employee o1, Employee o2) {
                    return o1.getLastName().compareTo(o2.getLastName());
                }
            });
            fireTableDataChanged();
        }

        public void add(Employee employee) {
            int rowCount = getRowCount();
            getEmployees().add(employee);
            fireTableRowsInserted(rowCount, rowCount);
        }

        public void delete(Employee employee) {
            List<Employee> employees = getEmployees();
            for (int index = 0; index < employees.size(); index++) {
                if (employees.get(index).equals(employee)) {
                    employees.remove(index);
                    fireTableRowsDeleted(index, index);
                    break;
                }
            }
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Employee employee = getEmployees().get(rowIndex);
            switch (columnIndex) {
                case 0:
                    return employee.getFirstName();
                case 1:
                    return employee.getLastName();
                case 2:
                    return employee.getSalary();
            }
            throw new ArrayIndexOutOfBoundsException(rowIndex + "x" + columnIndex + " is out of bounds");
        }
    }

    public class BubbleSort {
        public static <T> void sort(List<T> list, Comparator<? super T> comparator) {
            // Performing bubble sort
            for (int i = 0; i < list.size() - 1; i++) {
                // Looping through indexes
                for (int j = 0; j < list.size() - 1; j++) {
                    // Comapare the salaries of each indiviual and see if the previous is bigger than the next
                    if (comparator.compare(list.get(j), list.get(j + 1)) > 0) {
                        // Initializing a temp employee to hold value before switching
                        T temp = list.get(j);

                        // Swap the employees
                        list.set(j, list.get(j + 1));
                        list.set(j + 1, temp);
                    }
                }
            }
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thank you so much for this example, I've been able to build on it due to clarity in your coding! I just had one more question, how would I implement a search feature in this? In my example, I implemented a search feature using RowSorter. Is there a way I can use that to make a search tool here? I've tried to do it the same way I did it using my old code by passing `employeeTableModel` as the `model` but it doesn't update the table and I'm not really sure why. – Soccerball123 Feb 03 '23 at 06:04
  • In theory, if you wanted to do it yourself, then I'd use a proxy model, which wraps the original model and "filters" it based on your needs. This is not an uncommon solution and before we had the row sorter APIs, this is how we sorted `TableModel`s. For [example](https://stackoverflow.com/questions/27372678/filtering-jcombobox/27372985#27372985) (although this is a `JComboBox` based solution the basic principle remains the same) – MadProgrammer Feb 03 '23 at 07:08