开发者

Is is possible to make a method execute only once?

开发者 https://www.devze.com 2022-12-27 14:14 出处:网络
I have a for loop and structure like this: for(....) .... .... if(isTrue) ... do something.. .. method to be executed once (doTrick) is declared outside for loop.

I have a for loop and structure like this:

for(....)
....
....
if(isTrue)
... do something..
.. method to be executed once (doTrick) is declared outside for loop.
....endif
endfor

public void doTrick()
...
...
..end

Is it possible for a 开发者_开发技巧method in for loop to be executed only once?


Sure!..

if(!alreadyExecuted) {
    doTrick();
    alreadyExecuted = true;
}


Your can use AtomicBoolean to make sure the task is only called the first time:

import java.util.concurrent.atomic.AtomicBoolean;

public class Once {
    private final AtomicBoolean done = new AtomicBoolean();
    
    public void run(Runnable task) {
        if (done.get()) return;
        if (done.compareAndSet(false, true)) {
            task.run();
        }
    }
}

Usage:

Once once = new Once();
once.run(new Runnable() {
    @Override
    public void run() {
        foo();
    }
});

// or java 8
once.run(() -> foo());


if you use kotlin, you can do this:

val execOnce by lazy {
   print("hello, world")
}


In Java 8, you can effectively do this using automatic memoization as described here: Do it in Java 8: Automatic memoization

I'll admit that memoization could be considered overkill for a "run once" scenario, but it is a rather clean alternative to some described in previous answers.

For instance:

public void doSomething() { ... }

Map<Boolean, Boolean> cache = new ConcurrentHashMap<>();

public void doSomethingOnce() {
  cache.computeIfAbsent(true, x -> {
    doSomething();
    return true;
  });
}


You can avoid the if() by using this trick:

private Runnable once;
private final static Runnable NOP = new Runnable () {
    public void run () {
        // Do nothing
    }
}

public void method () {
    once = new Runnable () {
        public void run () {
            doTrick();
            once = NOP;
        }
    }

    for (...) {
        ...
        once.run();
        ...
    }
}


Another overkill solution:

Depending on what you want to do, it might be possible to use a static initialization block.

public class YourKlass{
    public void yourMethod(){

        DoTrick trick; 

        for( int i = 0; condition; i++){
            // ... (1)
            trick = new DoTrick(); // or any kind of accessing DoTrick
            // ... (2)
        }

    } 
}

public class DoTrick{
    static{
        // Whatever should be executed only once 
    }
}

Simple solution:

Or, instead you just want to execute the first part outside of the loop:

int i = 0;
if( condition ){
    // ... (1)
    // do trick
    // ... (2)
}
for(i = 1; condition; i++){
    // ... (1)
    // ... (2)
}


perhaps the break keyword is what you need? After running you method call break; I am sorry its not 100% clear what you mean from your question.

Have a look here from the sun docs


my sample from my app:

boolean alreadyExecuted = false;

then :

private void startMyService() {

    if(!alreadyExecuted) {

        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
          @Override
          public void run() {
            //Do something after 4 seconds
              context.startService(new Intent(context, myService.class));
              alreadyExecuted = true;
          }
        }, 4000);
       }


I want to do something slight more complex. In a multi-threaded environment ensure that the methods is run only one (which is solved by Hover Ruan's answer above), but also block any thread so none return until the method is done.

My solution is to use a Semaphore to do the blocking.

public class Once implements Runnable {
    private static Semaphore signal = new Semaphore(1,true);
    private static boolean done=false;  

    @Override
    public void run() {
        if(done)
            return;
        signal.acquireUninterruptibly();
        if(done) {
            signal.release();
            return;
        }
        doTrick(); // actually run the task
        done=true;
        signal.release();
        return;
    }

    static int result; // Result of running the task
    static int count;  // number of times its been called

    /**
     * The task we only want to run once
     */
    public void doTrick() {
        ++count;
        Random rnd = new Random();
        for(int i=0;i<10000;++i) {
            result += rnd.nextInt(100);
        }
        try {
            Thread.sleep(1000); // just to ensure all thread start
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for(int i=0;i<5;++i) { // multiple instances
            final Once task = new Once();
            for(int j=0;j<5;++j) { // multiple calls of same instance
                executor.submit(() -> {
                    task.run();
                    System.out.println("Result "+Once.result+" count "+Once.count);
                } );
            }
        }
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }           
    }
}

The program should pause, waiting until the first thread has finished, then all other threads should finish printing the result stored from the first run.


or by using Shared Preferences:

    sharedpref = PreferenceManager.getDefaultSharedPreferences(this);


isFirstRun = sharedpref.getBoolean("FIRSTRUN", true);

if (isFirstRun)
{

    // Do your unique code magic here

    SharedPreferences.Editor editor = wmbPreference.edit();
    editor.putBoolean("FIRSTRUN", false);
    editor.commit();
}else{
    //this will repeat everytime 
}


Here is an example way. Just use "new Getlineonce();"

class Getlineonce {
    static int[] linesRun = new int[0];

    public Getlineonce() {
        boolean lineRan = false;
        int line = Thread.currentThread().getStackTrace()[2].getLineNumber();
        for(int i = 0; i < linesRun.length; i++) {
            if(line == linesRun[i]) {
                lineRan = true; //Dont print
            }
        }

        if(!lineRan) {
            add(line);
            System.out.println(line + " ran!");
        }

    }

    public void add(int line) {
        int newSize = linesRun.length+1;
        int[] linesRunBuff = new int[newSize];
        for(int i = 0; i < newSize-1; i++) {
            linesRunBuff[i] = linesRun[i];
        }
        linesRunBuff[newSize-1] = line;
        linesRun = linesRunBuff;
    }

}


For me, the perfect solution was...

 public class MainActivity extends BaseActivity {
    private static boolean splash = false;

    if (!splash){
        runSplash();
    }

    private void runSplash(){
        MainActivity.splash = true;
    }
 }

Defined my variable as private static and use the access via class.variable in my function :)


Try this. First, this will be called only once when the app is being installed for the first time on the user's device. Use SharedPreferences this will help us to remember that this method has already been called so it will not re-call it again when the app is killed or even if the device was turned off. (But keep in mind that when the user un-stall and then re-install the app the method will be called again)

public class MainActivity extends AppCompatActivity {

    private static boolean alreadyExecuted = false; // Use private static boolean

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); // Use SharedPreferences
        if (!prefs.getBoolean("onlyonce", false)) {

            startMyService(); // Method to be called only once as long as the app wont be un-stalled but there is 
                              // no problem if the app will be killed or the device being turned off.

            SharedPreferences.Editor editor = prefs.edit();
            editor.putBoolean("onlyonce", true);
            editor.commit();
        }
    }

    private void startMyService() {

        if (!alreadyExecuted) {

            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    Intent intent = new Intent(MainActivity.this, SecondActivity2.class);
                    startActivity(intent);
                    alreadyExecuted = true;
                }
            }, 4000);
        }
    }


}


import java.util.ArrayList;

class RemoveDuplicate {
    public static void main(String[] args) {
        ArrayList<String> originalList = new ArrayList<String>();
        originalList.add("foo");
        originalList.add("bar");
        originalList.add("bat");
        originalList.add("baz");
        originalList.add("bar");
        originalList.add("bar");
        String str="bar";
        ArrayList<String> duplicateList = new ArrayList<String>();

        // adding duplicates to duplicateList
        for(String currentString : originalList) {
        if(currentString.startsWith(str)) {
                duplicateList.add(currentString);
            }
        }
        // removing duplicates in duplicatesList
        for(String removeString : duplicateList) {
            originalList.remove(removeString);
        }
        // finally adding duplicateElement
        originalList.add(str);

        for(String currEntry : originalList) {
            System.out.println(currEntry);
        }
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号