编程时我经常遇到的问题是如何处理未知数量的对象。通过处理,我的意思是引用它们,对其进行操作等。至于我,这将是在开发较小的游戏和程序时。
目前,我正在开发一个得分保持程序,该程序应显示球员的姓名,得分以及其他各种功能。此外,应该有两个按钮,可以在得分表中添加和删除球员,这就是我将在此处重点介绍的内容。它可能看起来像这样:
//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,因为它提供了一种基于对象的属性(例如字符串名称或类似名称)访问对象的简便方法。
答案 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
会更加容易和安全。但是,如果要命名变量player1
,player2
等,我认为您应该从数组开始。如果您希望更快地查找玩家,则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)
的实现。