用Java处理未知数量的对象

时间:2018-10-18 11:00:44

标签: java object

编程时我经常遇到的问题是如何处理未知数量的对象。通过处理,我的意思是引用它们,对其进行操作等。至于我,这将是在开发较小的游戏和程序时。

目前,我正在开发一个得分保持程序,该程序应显示球员的姓名,得分以及其他各种功能。此外,应该有两个按钮,可以在得分表中添加和删除球员,这就是我将在此处重点介绍的内容。它可能看起来像这样:

//A very limited version of my program
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class Application extends JFrame implements ActionListener{

    //Fields, variables and components
    Container mainCont = getContentPane();      //Main container, the window itself
    private JPanel buttonPanel;
    private JPanel namePanel;
    private JButton addPlayerButton;
    private JButton removePlayerButton;
    //...
    //Many more components

    public Application(){

        //Basic window initiation
        setTitle("Score Keeper");
        this.setSize(650, 700);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainCont.setBackground(Color.BLACK);
        this.setContentPane(mainCont);

        buttonPanel = new JPanel();
        namePanel = new JPanel();

        addPlayerButton = new JButton();
        addPlayerButton.addActionListener(this);
        buttonPanel.add(addPlayerButton);

        removePlayerButton = new JButton();
        removePlayerButton.addActionListener(this);
        buttonPanel.add(removePlayerButton);

        this.add(buttonPanel);
        this.add(namePanel);
        this.setVisible(true);

        //Other code omitted for now
        //Includes other graphic components, layout managers etc.

    }

    /*
    * Action-Listener. 
    * Performs an event on an action.
    */
    @Override
    public void actionPerformed(ActionEvent event){

        if(event.getSource() == addPlayerButton){
            Application.Player newPlayer = this.new Player();    //Creates a new object, i.e. a new player
            //See below for Player class
        }

        if(event.getSource() == removePlayerButton){
            //******
            // This is where the problem lies
        }

    }

    //I use a nested class to create a new player
    public class Player{

        //Components etc.

        private String name;
        private JLabel nameLabel;

        public Player(){

            name = getName();
            nameLabel = new JLabel(name);
            namePanel.add(nameLabel);


        }

        public String getName(){
            //This basically gets input from the user to assign a name to the new player
            //Code omitted for now

        }

    }

}

到目前为止,一切都很好。该程序基本上只有两个按钮,其中addPlayerButton添加一个播放器对象,该对象的名称显示在屏幕上。每次按下此按钮,都会在屏幕上添加一个新播放器。这可以无限次地完成。

问题是我们要删除播放器时出现的。我们该怎么做?我们无法按名称引用它,因为所有玩家对象实际上都是匿名的。

当然,替代方法是预定义固定数量的玩家对象:

class Application extends JFrame implements ActionListener{

    //Fields, variables and components
    Container mainCont = getContentPane();      //Main container, the window itself
    private JPanel buttonPanel;
    private JPanel namePanel;
    private JButton addPlayerButton;
    private JButton removePlayerButton;

    private Player player1;
    private Player player2;
    private Player player3;
    //...
    //Etc.

这时我们将能够直接寻址每个玩家对象,但这太不切实际了。我们不能添加超过预定数量的玩家,并且如果我们想要更少的玩家,我们会有一堆从未使用过的玩家对象。此外,我们必须对每个玩家的每次启动进行硬编码-每个nameLabel都必须手动添加到屏幕等。

请分享您对如何处理此类问题以及如何处理未知数量对象的知识。 感谢您抽出宝贵的时间和帮助!

P.S。我对这个论坛还很陌生。请让我知道是否有任何问题可以更改,等等。我进行了研究,发现之前没有解决此问题的方法,但是如果有一个我想念的问题,随时告诉我!

编辑1:好的。有很多很好的答案。我选择了使用哈希映射作为正确的解决方案,因为我认为这是我提供的前提的最佳解决方案。我实际解决问题的方式是,我在播放器对象中添加了一个JButton,该JButton删除了存储在其中的播放器对象。我还取消了为播放器使用嵌套类的概念,只是将其实现在单独的类中。 但是,我总体了解到的是,在处理对象时,您不知道对象的数量,通常最好将它们存储在某种类型的集合中。我的首选是Hashmap,因为它提供了一种基于对象的属性(例如字符串名称或类似名称)访问对象的简便方法。

4 个答案:

答案 0 :(得分:2)

您可以使用Map / Hashmap,并且每次创建播放器时,请将其添加到地图中。

您还必须从直接在屏幕上绘制玩家转变为在地图中绘制所有玩家,这样,当某个玩家从地图中删除时,将不再被绘制。

您将执行以下操作:

    Map<String, Player> map = new HashMap<>();
    map.put(player.Name, player);

然后您将在该哈希图中绘制所有内容。要删除,您只需要提供要删除的播放器的名称即可。

    map.remove(player.Name);

当然,然后您将需要稍微更改代码以在地图中呈现所有内容,我相信您需要一种方法来知道要删除哪个播放器,您可能想添加一个文本字段来输入地图名称播放器将被删除。

答案 1 :(得分:1)

如果要基于其名称删除def get_properties(self, mol): return '|'.join([Chem.MolToInchi(mol), Chem.MolToSmiles(mol, isomericSmiles=True), ...]) suppl = Chem.SDMolSupplier('chembl_24_1.sdf') string_with_results = '' for mol in suppl: if mol is not None: string_with_results = ''.join([string_with_results, get_properties(mol), '\n']) ,可以执行以下操作:

Player

您还可以轻松地将新玩家添加到列表中:

// Create a list of players, which you can define globally
ArrayList<Player> players = new ArrayList<>();

// The name of the player to find
String name = "theNameOfThePlayerToFind";

// Loop through the players and remove the player with the given name
for (Player player : players) {
    if (player.getName().equals(name)) {
        players.remove(player);
    }
}

答案 2 :(得分:1)

我会@Katada Freije使用HashMap的方法。稍微说明一下,您基本上有一个Players集合,其名称为键。然后,您可以使用键删除Player

但是我也可以避免这种情况,因为在某些情况下,多个Players具有相同的名称。我会选择List<Player>。这样Player将由索引而不是名称定义。然后,您可以使用索引来删除带有内置methods的播放器。

答案 3 :(得分:0)

让我们假设您正在使用JList来显示当前玩家。 Java的Swing将模型(实际存储显示的对象)和视图和控件(显示它们的JList)分开。这种设计称为MVC,非常常见。

我们用不同的方式存储实际的Player对象。选择哪种取决于您计划如何操纵玩家。最简单的方法是使用数组,但这仅在您永远不会有太多玩家的情况下起作用:

 Player[] players = new Player[MAX_PLAYERS](); // define MAX_PLAYERS somewhere
 int currentPlayers = 0;                       // always < MAX_PLAYERS

要将其公开给JList,您可以按如下方式使用自定义适配器模型(在可访问players数组的内部类中):

 private final class PlayerListModel extends AbstractListModel<Player> {
     @Override
     Player getElementAt(int position) { return players[position]; }
     @Override
     int getSize() { return currentPlayers; }
 }

然后可以在构造时将其传递给JList:

 private PlayerListModel playerListModel = new PlayerListModel();
 private JList playersListView = new JList(playerListModel);

现在,要按名称删除玩家,您将首先更新模型,然后刷新视图:

 private void removePlayerByName(String name) {
     int toRemove = -1;
     for (int i=0; i<currentPlayers; i++) {
        Player p = players[i];
        if (p.getName().equals(name)) {
           toRemove = i;
           break;
        }
     }
     if (toRemove != -1) {
        // update model
        currentPlayers --; 
        players[toRemove] = players[currentPlayers];

        // update views
        playerListModel.fireContentsChanged(this, toRemove, currentPlayers);           
     }
 }

代替players数组,使用ArrayList<Player> players会更加容易和安全。但是,如果要命名变量player1player2等,我认为您应该从数组开始。如果您希望更快地查找玩家,则TreeMap<String, Player>可以使他们按名称排序并易于查找。在两种情况下,您都必须相应地更新模型和removePlayerByName函数。例如,如果使用TreeMap,它将更短(更快):

 private void removePlayerByName(String name) {
    if (players.containsKey(name)) {
      players.remove(name);
      playerListModel.fireContentsChanged(this, 0, currentPlayers);           
    }
 }    

另一方面,在您要删除的播放器上单击以选择它,然后单击remove按钮来进行操作时,通常会找到接口。您可以知道使用以下代码选择了哪个玩家:

选择的玩家= playersListView.getSelectedValue();

如果有选择(selected != null),则可以调用removePlayerByName(selected.getName()),或者甚至更好,编写一个不依赖名称但依赖于(当前缺少)的removePlayer(Player p) Player.equals(Player another)的实现。