当我想从树集中删除 object 时,为什么会出现不受支持的操作异常
why do i get an unsupported operation exception when i want to remove an object from a treeset
我有这个 3 class :
第一个
private List<Domino> pile = new ArrayList<Domino>();
public DominoPile(List<Domino> list) {
this.pile = list;
}
public List<Domino> getList(){
return List.copyOf(pile);
}
public void getOutLastOne() {
pile.remove(this.getLast(0));
}
public Domino getLast(int nbr) {
return nbr == 0 || nbr > this.getSize() ? pile.get(pile.size() - 1) : pile.get(pile.size() - 1 - nbr);
}
public int getSize() {
return pile.size();
}
}
第二个
public class KingdominoGameFactory {
private List<Player> pl;
private List<Domino> dom;
private int selected;
private DominoPile pile;
private int nbrPl;
public KingdominoGameFactory(List<Player> p, List<Domino> d) {
// TODO Auto-generated constructor stub
this.setPl(List.copyOf(p));
this.setDom(List.copyOf(d));
}
public List<Player> getPl() {
return pl;
}
public void setPl(List<Player> pl) {
this.pl = pl;
}
public List<Domino> getDom() {
return dom;
}
public void setDom(List<Domino> dom) {
this.dom = dom;
}
public int getSelected() {
return selected;
}
public void setSelected(int selected) {
this.selected = selected;
}
public int getNbrPl() {
return nbrPl;
}
public void setNbrPl() {
if(selected == 0) this.nbrPl = 2;
else if(selected == 1) this.nbrPl = 3;
else this.nbrPl = 4;
}
public DominoPile getPile() {
return pile;
}
private void setPile(DominoPile pile) {
this.pile = pile;
}
public void nbrDomFinal() {
if(this.getNbrPl() == 2) this.setPile(new DominoPile(this.getDom().subList(0, 24)));
else if(this.getNbrPl() == 3) this.setPile(new DominoPile(this.getDom().subList(0, 36)));
else this.setPile(new DominoPile(this.getDom()));
}
public void nbrPlFinal() {
if(this.getNbrPl() == 2) this.setPl(this.getPl().subList(0, 2));
else if(this.getNbrPl() == 3) this.setPl(this.getPl().subList(0, 3));
else this.setPl(this.getPl());
}
第三个
public class Game {
private List<Player> players;
private DominoPile pile;
private DrawLine actual = new DrawLine(new TreeSet<Domino>());
private final int nbrDraw;
public Game(KingdominoGameFactory kg){
this.nbrDraw = kg.getNbrPl() == 3 ? 3 : 4;
this.pile = new DominoPile(List.copyOf(kg.getPile().getList()));
this.players = this.getListPlayers();
}
private void addActual(){
actual.add(pile.getLast(0));
}
public int getNbrDraw() {
return nbrDraw;
}
public List <Player> getListPlayers(){
return players;
}
public void setDrawActual() {
actual.clear();
for(int i = 0; i < nbrDraw; ++i) {
this.addActual();
pile.getOutLastOne();
}
}
public DrawLine getActual() {
return actual;
}
public DominoPile getPile() {
return pile;
}
}
那我就做了这个测试
@BeforeEach
void setup() {
List<Domino> liste = new ArrayList<Domino>();
liste.add(new Domino(1, new Tile(Terrain.CASTLE, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(2, new Tile(Terrain.CASTLE, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(3, new Tile(Terrain.CASTLE, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(4, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(5, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(6, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(7, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(8, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(9, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(10, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(11, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(12, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(13, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(14, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(15, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(16, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(17, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(18, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(19, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(20, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(21, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(22, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(23, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(24, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(25, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
List<Player> liste2 = new ArrayList<Player>();
liste2.add(new Player("Jeff", "366"));
liste2.add(new Player("Jeff", "366"));
liste2.add(new Player("Jeff", "366"));
liste2.add(new Player("Jeff", "366"));
kg = new KingdominoGameFactory(liste2, liste);
kg.setSelected(0);
kg.setNbrPl();
kg.nbrDomFinal();
kg.nbrPlFinal();
game = new Game(kg);
}
@Test
void setActual() {
game.setDrawActual();
assertEquals(game.getActual().getSize(), 4);
}
然后我有这个错误
java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:72)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.remove(ImmutableCollections.java:79)
at kingdomino.domains.DominoPile.getOutLastOne(DominoPile.java:23)
at kingdomino.domains.Game.setDrawActual(Game.java:35)
at kingdomino.domains.GameTest.setActual(GameTest.java:87)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
我不知道为什么我不能从游戏中移除 class 因为 DominoPile 的测试都成功了,这是唯一有问题的测试,我不知道如何解决这个问题我试图改变我的 collection 类型,我也试图改变我的方法“getOutLastOne”
List.copyOf
这会生成一个 不可变(即:只读)副本,无论您传递给它什么。复制这个没有进一步的意义(事实上,我相信如果你在其中传递一个 already-immutable 列表,它只是逐字地 returns ;复制无法修改的东西没有意义).
创建 DominoPile 对象时,将 List.copyOf
的结果传递给其构造函数,这意味着 DominoPile 中的 pile
字段现在是这些不可变列表之一。稍后您的代码调用 getOutLastOne
修改(变异)由 pile
字段引用的列表,这不起作用,因为它无法修改。如例外情况所述。
这就是问题所在。您可以使用 3 种不同的策略来解决问题。概括地说:
DominoPile
的构造函数中的文档,passed-in 列表被逐字记录并将按原样修改,意思是:它不能是不可变的(现在调用者有责任阅读文档并按照它说的去做),以及 DominoPile 对列表造成的任何影响(例如删除最后一个)回显到用于首先创建 DominoPile 对象的列表。
完成后(基本上,您需要做的就是添加一些 javadoc),然后是 caller(您当前有 new DominoPile(List.copyOf(kg....))
)需要停止传递不可变列表。这很简单 - 将 List.copyOf(X)
替换为 new ArrayList<>(X)
- 这也制作了一个副本,但是 mutable 一个。
或者,您也可以选择稍微不同的设计:让 DominoPile 仅将提供的列表视为模板,从而让您的 DominoPile 构造函数创建一个新的(可变的!)列表,该列表是一个副本它的。换句话说,将构造函数和 set
方法中的 this.pile = pile
替换为 this.pile = new ArrayList<>(pile)
。但我有点不喜欢这个名字 setPile
如果是这样的话。
DominoPile 变得不可变,所有设置方法都需要消失,所有数据结构都变得不可变,并且像 .getOutLastOne
这样的方法实际上不会删除任何东西(它不能 - 一切无法修改),而是创建一个全新的 DominoPile 对象,该对象是自身的克隆,只是删除了一个多米诺骨牌。现在,您的列表是不可变的这一事实(如果有的话)是一个好处。这是... over-engineering 我认为有相当大的差距。
这些选项是按照我会怎么做的顺序列出的 - 所以,除非你有一个紧迫的理由为什么第一个选项对你来说不好,否则我会选择第一个,在这里。基于你的 API 设计(比如有一个 set
方法,这意味着你似乎并不特别渴望不变性)。
我有这个 3 class :
第一个
private List<Domino> pile = new ArrayList<Domino>();
public DominoPile(List<Domino> list) {
this.pile = list;
}
public List<Domino> getList(){
return List.copyOf(pile);
}
public void getOutLastOne() {
pile.remove(this.getLast(0));
}
public Domino getLast(int nbr) {
return nbr == 0 || nbr > this.getSize() ? pile.get(pile.size() - 1) : pile.get(pile.size() - 1 - nbr);
}
public int getSize() {
return pile.size();
}
}
第二个
public class KingdominoGameFactory {
private List<Player> pl;
private List<Domino> dom;
private int selected;
private DominoPile pile;
private int nbrPl;
public KingdominoGameFactory(List<Player> p, List<Domino> d) {
// TODO Auto-generated constructor stub
this.setPl(List.copyOf(p));
this.setDom(List.copyOf(d));
}
public List<Player> getPl() {
return pl;
}
public void setPl(List<Player> pl) {
this.pl = pl;
}
public List<Domino> getDom() {
return dom;
}
public void setDom(List<Domino> dom) {
this.dom = dom;
}
public int getSelected() {
return selected;
}
public void setSelected(int selected) {
this.selected = selected;
}
public int getNbrPl() {
return nbrPl;
}
public void setNbrPl() {
if(selected == 0) this.nbrPl = 2;
else if(selected == 1) this.nbrPl = 3;
else this.nbrPl = 4;
}
public DominoPile getPile() {
return pile;
}
private void setPile(DominoPile pile) {
this.pile = pile;
}
public void nbrDomFinal() {
if(this.getNbrPl() == 2) this.setPile(new DominoPile(this.getDom().subList(0, 24)));
else if(this.getNbrPl() == 3) this.setPile(new DominoPile(this.getDom().subList(0, 36)));
else this.setPile(new DominoPile(this.getDom()));
}
public void nbrPlFinal() {
if(this.getNbrPl() == 2) this.setPl(this.getPl().subList(0, 2));
else if(this.getNbrPl() == 3) this.setPl(this.getPl().subList(0, 3));
else this.setPl(this.getPl());
}
第三个
public class Game {
private List<Player> players;
private DominoPile pile;
private DrawLine actual = new DrawLine(new TreeSet<Domino>());
private final int nbrDraw;
public Game(KingdominoGameFactory kg){
this.nbrDraw = kg.getNbrPl() == 3 ? 3 : 4;
this.pile = new DominoPile(List.copyOf(kg.getPile().getList()));
this.players = this.getListPlayers();
}
private void addActual(){
actual.add(pile.getLast(0));
}
public int getNbrDraw() {
return nbrDraw;
}
public List <Player> getListPlayers(){
return players;
}
public void setDrawActual() {
actual.clear();
for(int i = 0; i < nbrDraw; ++i) {
this.addActual();
pile.getOutLastOne();
}
}
public DrawLine getActual() {
return actual;
}
public DominoPile getPile() {
return pile;
}
}
那我就做了这个测试
@BeforeEach
void setup() {
List<Domino> liste = new ArrayList<Domino>();
liste.add(new Domino(1, new Tile(Terrain.CASTLE, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(2, new Tile(Terrain.CASTLE, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(3, new Tile(Terrain.CASTLE, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(4, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(5, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(6, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(7, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(8, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(9, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(10, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(11, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(12, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(13, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(14, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(15, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(16, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(17, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(18, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(19, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(20, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(21, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(22, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(23, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(24, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
liste.add(new Domino(25, new Tile(Terrain.EMPTY, 2), new Tile(Terrain.CASTLE, 1)));
List<Player> liste2 = new ArrayList<Player>();
liste2.add(new Player("Jeff", "366"));
liste2.add(new Player("Jeff", "366"));
liste2.add(new Player("Jeff", "366"));
liste2.add(new Player("Jeff", "366"));
kg = new KingdominoGameFactory(liste2, liste);
kg.setSelected(0);
kg.setNbrPl();
kg.nbrDomFinal();
kg.nbrPlFinal();
game = new Game(kg);
}
@Test
void setActual() {
game.setDrawActual();
assertEquals(game.getActual().getSize(), 4);
}
然后我有这个错误
java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:72)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.remove(ImmutableCollections.java:79)
at kingdomino.domains.DominoPile.getOutLastOne(DominoPile.java:23)
at kingdomino.domains.Game.setDrawActual(Game.java:35)
at kingdomino.domains.GameTest.setActual(GameTest.java:87)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
我不知道为什么我不能从游戏中移除 class 因为 DominoPile 的测试都成功了,这是唯一有问题的测试,我不知道如何解决这个问题我试图改变我的 collection 类型,我也试图改变我的方法“getOutLastOne”
List.copyOf
这会生成一个 不可变(即:只读)副本,无论您传递给它什么。复制这个没有进一步的意义(事实上,我相信如果你在其中传递一个 already-immutable 列表,它只是逐字地 returns ;复制无法修改的东西没有意义).
创建 DominoPile 对象时,将 List.copyOf
的结果传递给其构造函数,这意味着 DominoPile 中的 pile
字段现在是这些不可变列表之一。稍后您的代码调用 getOutLastOne
修改(变异)由 pile
字段引用的列表,这不起作用,因为它无法修改。如例外情况所述。
这就是问题所在。您可以使用 3 种不同的策略来解决问题。概括地说:
DominoPile
的构造函数中的文档,passed-in 列表被逐字记录并将按原样修改,意思是:它不能是不可变的(现在调用者有责任阅读文档并按照它说的去做),以及 DominoPile 对列表造成的任何影响(例如删除最后一个)回显到用于首先创建 DominoPile 对象的列表。
完成后(基本上,您需要做的就是添加一些 javadoc),然后是 caller(您当前有 new DominoPile(List.copyOf(kg....))
)需要停止传递不可变列表。这很简单 - 将 List.copyOf(X)
替换为 new ArrayList<>(X)
- 这也制作了一个副本,但是 mutable 一个。
或者,您也可以选择稍微不同的设计:让 DominoPile 仅将提供的列表视为模板,从而让您的 DominoPile 构造函数创建一个新的(可变的!)列表,该列表是一个副本它的。换句话说,将构造函数和
set
方法中的this.pile = pile
替换为this.pile = new ArrayList<>(pile)
。但我有点不喜欢这个名字setPile
如果是这样的话。DominoPile 变得不可变,所有设置方法都需要消失,所有数据结构都变得不可变,并且像
.getOutLastOne
这样的方法实际上不会删除任何东西(它不能 - 一切无法修改),而是创建一个全新的 DominoPile 对象,该对象是自身的克隆,只是删除了一个多米诺骨牌。现在,您的列表是不可变的这一事实(如果有的话)是一个好处。这是... over-engineering 我认为有相当大的差距。
这些选项是按照我会怎么做的顺序列出的 - 所以,除非你有一个紧迫的理由为什么第一个选项对你来说不好,否则我会选择第一个,在这里。基于你的 API 设计(比如有一个 set
方法,这意味着你似乎并不特别渴望不变性)。