简体   繁体   English

在方法之间共享原始数据?

[英]Sharing primitive data among methods?

Say we have a public method "method," which uses private "subMethod1" and private "subMethod2" in its calculation. 假设我们有一个公共方法“ method”,它在计算中使用私有“ subMethod1”和私有“ subMethod2”。 These subMethods serve no other purpose than to break down "method" into more readable, encapsulated sub-parts, and to avoid code duplication within method. 这些子方法无非是为了将“方法”分解为更易读的,封装的子部分,并避免方法内的代码重复。

We need each of these sub methods to operate on 2 or more primitive data types, which are local variables defined at the top of method. 我们需要这些子方法中的每一个都可对2种或更多种原始数据类型进行操作,它们是在方法顶部定义的局部变量。 We cannot pass them into the sub methods by reference, since java does not allow primitives to be passed by reference. 我们不能通过引用将它们传递给子方法,因为java不允许基元通过引用传递。

So our solution is to refactor, and change the local variables defined at the top of method to member variables, which can of course be read and written by both method and its sub-methods. 因此,我们的解决方案是重构方法,并将在方法顶部定义的局部变量更改为成员变量,当然,方法及其子方法均可读取和写入成员变量。 The problem is that now we have member variables that don't really exist to store object state, but only exist, effectively, during the execution of method. 问题在于,现在我们的成员变量实际上并不存在以存储对象状态,而仅在方法执行期间有效地存在。 Of course, in actuality they exist throughout the whole lifetime of the object, but they are only ever used during the execution of method, and they could vanish at other times without affecting the behavior of the object at all (keep in mind that method initializes the members to default values at the beginning of its method body). 当然,实际上它们存在于对象的整个生命周期中,但是它们仅在方法执行期间使用过,并且它们可能在其他时间消失,而根本不影响对象的行为(请记住,方法会初始化成员在其方法主体的开头使用默认值)。

Well, you say, just wrap the primitives in objects, keep them local within method, then have "method" pass those objects to its submethods, where they will be able to change its value since it was passed by reference as all objects are. 好吧,您说过,只需将原语包装在对象中,将其保留在方法中的局部位置,然后让“方法”将这些对象传递给其子方法,在这些方法中,它们将能够更改其值,因为所有对象都是通过引用传递的。 The problem is that "method" is used inside a simulation loop, where it will be run millions of times. 问题是在模拟循环中使用了“方法”,该方法将运行数百万次。 The object wrappers slow things down noticeably, and performance is key here. 对象包装器明显降低了速度,而性能是关键。

So my question is: Is there any solution that will give me the same performance as my ugly "use members to share primitive data between methods" solution, but will also have good OO design? 因此,我的问题是:是否有任何解决方案可以使我获得与丑陋的“使用成员在方法之间共享原始数据”解决方案相同的性能,但还具有良好的OO设计? Would an inner class wrapping the method, sub-methods, and primitive data be appropriate? 包含方法,子方法和原始数据的内部类是否合适?

Thanks, Jonah 谢谢乔纳

EDIT: Here is a cooked up code sample whose sole purpose is demonstrate the slowdown caused by using object wrappers, as described above: 编辑:这是一个精练的代码示例,其唯一目的是演示如上所述使用对象包装程序导致的速度下降:

public class TestSharedMembers {

protected int m17Multiples;
protected int m23Multiples;

public int methodPrimitive(int i) {

    m17Multiples = 0;
    m23Multiples = 0;

    calc17Primitive(i);
    calc23Primitive(i);

    return m17Multiples + m23Multiples;
}

private void calc17Primitive(int i) {
    if (i % 17 == 0) 
        m17Multiples++;
}
private void calc23Primitive(int i) {
    if (i % 23 == 0) 
        m23Multiples++;
}



/// using object wrappers
class IntWrapper {
    private int mInt;
    public IntWrapper(int i) {
        mInt = i;
    }
    public int getInt() {
        return mInt;
    }
    public void increment() {
        mInt++;
    }
}
public int methodObject(int i) {

    IntWrapper o17 = new IntWrapper(0);
    IntWrapper o23 = new IntWrapper(0);

    calc17Object(i, o17);
    calc23Object(i, o23);

    return o17.getInt() + o23.getInt();
}

private void calc17Object(int i, IntWrapper o) {
    if (i % 17 == 0) 
        o.increment();
}
private void calc23Object(int i, IntWrapper o) {
    if (i % 23 == 0) 
        o.increment();
}


public static void main(String[] args) {
    TestSharedMembers t = new TestSharedMembers();
    final int NUM_ITERS = 20000000;

    double start = System.currentTimeMillis();
    int total = 0;
    for (int i=0; i<NUM_ITERS; i++) {
        total += t.methodPrimitive(i);
    }
    double stop = System.currentTimeMillis();
    System.out.println(total);
    System.out.println(stop - start);

    start = System.currentTimeMillis();
    total = 0;
    for (int i=0; i<NUM_ITERS; i++) {
        total += t.methodObject(i);
    }
    stop = System.currentTimeMillis();
    System.out.println(total);
    System.out.println(stop - start);

}
}

EDIT 2: Another example to test Jon's theory. 编辑2:另一个测试乔恩理论的例子。

public class TestSharedMembers {

protected int m17Multiples;
protected int m23Multiples;

public int methodPrimitive(int i) {

    m17Multiples = 0;
    m23Multiples = 0;

    calc17Primitive(i);
    calc23Primitive(i);

    return m17Multiples + m23Multiples;
}

private void calc17Primitive(int i) {
    if (i % 17 == 0) 
        m17Multiples++;
    if (i % 19 == 0) 
        m17Multiples++;
    if (i % 5 == 0) 
        m17Multiples++;
    if (i % 43 == 0) 
        m17Multiples++;
    if (i % 41 == 0) 
        m17Multiples++;
    if (i % 91 == 0) 
        m17Multiples++;
}
private void calc23Primitive(int i) {
    if (i % 23 == 0) 
        m23Multiples++;
    if (i % 17 == 0) 
        m23Multiples++;
    if (i % 19 == 0) 
        m23Multiples++;
    if (i % 5 == 0) 
        m23Multiples++;
    if (i % 43 == 0) 
        m23Multiples++;
    if (i % 41 == 0) 
        m23Multiples++;
    if (i % 91 == 0) 
        m23Multiples++;
}



/// using object wrappers
class IntWrapper {
    private int mInt1;
    private int mInt2;
    public IntWrapper(int i1, int i2) {
        mInt1 = i1;
        mInt2 = i2;
    }
    public int getInt1() {
        return mInt1;
    }
    public int getInt2() {
        return mInt2;
    }
    public void increment1() {
        mInt1++;
    }
    public void increment2() {
        mInt2++;
    }
}
public int methodObject(int i) {

    IntWrapper o = new IntWrapper(0,0);

    calc17Object(i, o);
    calc23Object(i, o);

    return o.getInt1() + o.getInt2();
}

private void calc17Object(int i, IntWrapper o) {
    if (i % 17 == 0) 
        o.increment1();
    if (i % 19 == 0) 
        o.increment1();
    if (i % 5 == 0) 
        o.increment1();
    if (i % 43 == 0) 
        o.increment1();
    if (i % 41 == 0) 
        o.increment1();
    if (i % 91 == 0) 
        o.increment1();
}
private void calc23Object(int i, IntWrapper o) {
    if (i % 23 == 0) 
        o.increment2();
    if (i % 17 == 0) 
        o.increment1();
    if (i % 19 == 0) 
        o.increment1();
    if (i % 5 == 0) 
        o.increment1();
    if (i % 43 == 0) 
        o.increment1();
    if (i % 41 == 0) 
        o.increment1();
    if (i % 91 == 0) 
        o.increment1();
}


public static void main(String[] args) {
    TestSharedMembers t = new TestSharedMembers();
    final int NUM_ITERS = 20000000;

    double start = System.currentTimeMillis();
    int total = 0;
    for (int i=0; i<NUM_ITERS; i++) {
        total += t.methodPrimitive(i);
    }
    double stop = System.currentTimeMillis();
    System.out.println(total);
    System.out.println(stop - start);

    start = System.currentTimeMillis();
    total = 0;
    for (int i=0; i<NUM_ITERS; i++) {
        total += t.methodObject(i);
    }
    stop = System.currentTimeMillis();
    System.out.println(total);
    System.out.println(stop - start);

}
}

I would look at creating a nested type which encapsulates all these currently-local variables, yes. 我会看一下如何创建一个嵌套类型来封装所有这些当前局部变量,是的。 Whether you then move the submethods into that type, or just keep the methods where they are and pass around a reference to an instance of this state class to the helper methods is up to you. 然后将子方法移动该类型中,还是将方法保持在原处,并将对这个状态类的实例的引用传递给辅助方法,则取决于您。 You may find it slightly simpler to unit test the submethods if they're declared within the state class, but obviously the two solutions are really equivalent. 如果在状态类中声明了子方法,则可能会发现对子方法进行单元测试稍微简单一些,但是显然,这两种解决方案实际上是等效的。

Do the submethods also need to access state in the existing class? 子方法是否还需要访问现有类中的状态? If so, I'd probably keep the submethods in the existing class - I suspect that will keep the code clearer. 如果是这样,我可能会将子方法保留在现有的类中-我怀疑这样会使代码更清晰。 I'm not a fan of true "inner" classes (with an implicit reference to the outer class) - I generally make my nested classes static, and do everything explicitly. 我不喜欢真正的“内部”类(带有对外部类的隐式引用)-我通常将嵌套类设为静态,并显式地执行所有操作。

(Note that "objects are passed by reference" is a mis-statement of what happens in Java. The reality is that references are passed by value, which is subtly different. Java doesn't have any pass-by-reference semantics.) (请注意,“对象是通过引用传递的”是对Java中发生的情况的错误陈述。现实是引用是通过值传递的,这有细微的差别。Java没有任何按引用传递的语义。)

Rather than wrapping individual primitives or adding the primitives as fields in the class the method is in, could you instead make this method itself an object with primitive fields? 而不是包装单个基元或将基元添加为方法所在的类中的字段,您是否可以使此方法本身成为具有基元字段的对象?

If the class where you currently have method() defined is Foo , rather than adding the primitives it uses to Foo make another class (an inner class, perhaps) FooMethod and, where you would have called method() before, do new FooMethod().method() instead. 如果当前定义了method()的类是Foo ,而不是将其使用的原语添加到Foo创建另一个类(也许是一个内部类) FooMethod并且,如果您之前曾调用过method() ,请执行new FooMethod().method()代替。 The primitive fields in FooMethod will then effectively be local variables since the FooMethod object is free to be GCed once the method call is finished. FooMethod的原始字段将有效地成为局部变量,因为一旦方法调用完成,就可以自由地对FooMethod对象进行GC。

public class ShareLocal {

private final static class methods
{

    private int sharedInt;
    public void method(int init)
    {
    sharedInt=init; 
    ... 
    }
    private void meth1()
    {
    ... 
    }
    private void meth2()
    {
    ... 
    }
}
private ShareLocal.methods methodCall=new ShareLocal.methods();
void simulationLoop()
{
for(int i=0;i<10;i++)   
  methodCall.method(i);
}


}

I think the inner class as you mentioned is the best way. 我认为您提到的内层阶级是最好的方法。

If you really regard a performance loss due to a wrapper object as a concern, then I would recommend not to extract the said 2 submethods. 如果您确实考虑到由于包装对象而导致的性能损失,那么我建议您不要提取上述2个子方法。 Large methods are bad smell, but if you need it for a simulation then "beautiful" object-oriented design might be 2nd priority. 大型方法难闻的气味,但是如果您需要进行模拟,则“美丽”的面向对象设计可能是第二优先级。

Anyway, don't save intermediary state in member variables. 无论如何, 不要将中间状态保存在成员变量中。 First, because what you've already mentioned, second, because it also slows down your code, and third, because if you ever plan to have multiple threads execute the methods, you're lost with race conditions. 首先,因为您已经提到过,其次,因为它也使代码运行速度变慢,其次,因为如果您打算让多个线程执行这些方法,您将迷失竞争条件。

Perhaps you could explain again why it is so important to you to have the fields vanish? 也许您可以再解释一次,为什么失去田地对您如此重要? If performance is as important as you say, why add this as a "requirement"? 如果性能与您所说的一样重要,为什么将其添加为“要求”? It appears you already have a solution which avoids the cost of object creation. 看来您已经有了一种避免对象创建成本的解决方案。

If you want a more optimial solution, use arguments and return values. 如果您想要更优化的解决方案,请使用参数和返回值。

public class TestSharedMembers { 
  public int methodPrimitive(int i) { 
    return calc17Primitive(i) + calc23Primitive(i); 
  } 
  private int calc17Primitive(int i) { 
    return i % 17 == 0 ? 1 : 0;
  } 
  private void calc23Primitive(int i) { 
    return i % 23 == 0? 1 : 0;
  } 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM