有没有一种很好的方法可以使用set / getPreferredSize,set / getMinimumSize和set / getMaximumSize(或绕过它们的方法),而不是偶尔分配Dimension对象? (除了扩展JComponents)
我的应用程序在鼠标拖动时大量使用此方法来调整大小/移动,我担心所有的Dimension对象。
我知道像set / getBounds这样的方法可以处理传递给它们的已经分配的对象,我似乎无法找到其他方法的等价物。
答案 0 :(得分:3)
是否有使用
的好方法set/getPreferredSize, set/getMinimumSize, and set/getMaximumSize
是的:不要使用它们!请改用相应的LayoutManager
。 LayoutManager负责计算这些值,因此最好的方法是将其委托给LayoutManager
。 BorderLayout
和GridBagLayout
已经为布局提供了一个非常好的起点。您可能不时使用FlowLayout
和/或GridLayout
。如果您愿意使用第三方库,MigLayout
也是有效的LayoutManager
。
请考虑阅读answer关于Should I avoid the use of set[Preferred|Maximum|Minimum]Size methods in Java Swing?
的内容无论如何,Dimension
对象的内存占用非常小,并且在实例化后很快就会被垃圾收集,所以你不必担心它们。
如果您认为自己遇到了性能问题,请使用分析器来帮助您找到它们。
答案 1 :(得分:2)
你必须每秒至少分配数千个这些对象,甚至开始担心。 HotSpot的GC方案专门针对短期对象的低成本进行了优化。它们在Eden空间中被分配,如果在Eden填满时没有幸存者,则只需更新指针即可释放所有幸存者。所以最好的建议是先关注其他性能问题;这个应该有一个非常低的优先级。
答案 2 :(得分:1)
“我很担心所有的Dimension对象。”
不要担心几千个快速死亡的微物体。 GC可以解决这个问题。如果遇到任何性能问题,则无法通过避免一些Dimension创建来修复它们。您应该知道,像setPreferredSize()这样的调用内部的成本要比作为参数传递的那么微小的Dimension高得多(查看JRE源代码,它执行更多内部)。
答案 3 :(得分:0)
我对JComponent的get / setPreferredSize机制和分配行为感兴趣
注意:我不对以下内容感兴趣:默认布局管理器如何工作。让我们想一下,我将preferredSize值用于与默认布局管理器无关的X目的。
官方文档未能给出解决方案,所以让我们看一下JRE源代码:
这个叫超级......
//JComponent.java (1642 ~ 1663)
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
Dimension size = null;
if (ui != null) {
size = ui.getPreferredSize(this);
}
return (size != null) ? size : super.getPreferredSize();
}
这个也叫超级......
//JComponent.java (1628 ~ 1639)
public void setPreferredSize(Dimension preferredSize) {
super.setPreferredSize(preferredSize);
}
JComponent扩展Container,让我们来看看:
//Container.java (1751 ~ 1800)
public Dimension getPreferredSize() {
return preferredSize();
}
@Deprecated
public Dimension preferredSize() {
Dimension dim = prefSize;
if (dim == null || !(isPreferredSizeSet() || isValid())) {
synchronized (getTreeLock()) {
prefSize = (layoutMgr != null) ?
layoutMgr.preferredLayoutSize(this) :
super.preferredSize();
dim = prefSize;
}
}
if (dim != null){
return new Dimension(dim);
}
else{
return dim;
}
}
正如我想的那样,它正在返回一个新实例。但是让我们看一下使用set.java中的setter会发生什么(因为Container扩展了Component但是没有覆盖该方法):
//Component.java (2560 ~ 2585)
public void setPreferredSize(Dimension preferredSize) {
Dimension old;
if (prefSizeSet) {
old = this.prefSize;
}
else {
old = null;
}
this.prefSize = preferredSize;
prefSizeSet = (preferredSize != null);
firePropertyChange("preferredSize", old, preferredSize);
}
这只是引用我传递的对象!,完美! (确实很危险,但它符合我目前的需求!)
我总结说我正在设置我的组件保存和使用的实际对象实例,所以(至少对于这个JRE版本),不需要再次使用相同的对象重复调用setPreferredSize(但是没有这样做会确实很危险,因为这个源代码可以在不同的实现中改变),即使如此,如果我们使用相同的Dimension实例调用它, setPreferredSize也不会生成垃圾。另一方面,调用getPreferredSize显然会分配一个新的Dimension对象来返回值。
答案是:在某处跟踪您的首选大小以最小化getPreferredSize的使用,并且对API有礼貌,调用setPreferredSize,但使用相同的Dimension实例来避免垃圾。< / p>
Dimension pref = null;
void getNewPrefOnlyWhenNeeded() {
pref = component.getPreferredSize();
}
void calledEveryMillisecond() {
//Here or on the Constructor, MouseListener or wherever
if (pref == null || someCondition) getNewPrefOnlyWhenNeeded();
// do stuff with pref...
...
//setPreferredSize can actually be skipped if we call our layout manager after all is resized. It would be dirty tho...
component.setPreferredSize(pref);
}
经过进一步分析,最大和最小尺寸方法共享这种机制,所以一切都解决了。