Home > Blockchain >  How to run a map of runnables in order
How to run a map of runnables in order

Time:10-04

I'm trying to make a method that runs a series of steps defined by lambdas. The following code illustrates how I am doing it. However, the steps aren't executed in order. I thought about using LinkedHashMap instead of Map, but the lambda says Target type of a lambda conversion must be an interface.

public class Test {
    static void runSomeStuff(Map<String, CheckedRunnable> actions){
        actions.forEach((name, action) -> {
            System.out.println("Running "   name   "...");
            try {
                action.run();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        });
    }
    public static void main(String[] args) {
        runSomeStuff(Map.of(
                "Step 1", () -> {
                    System.out.println("1");
                },
                "Step 2", () -> {
                    System.out.println("2");
                }
                ));
    }
}

CodePudding user response:

You've not shared CheckedRunnable but if you've declared such that your Map.of works, then you should be able to pass in LinkedHashMap such that the order of actions is preserved:

LinkedHashMap<String,CheckedRunnable> actions = new LinkedHashMap<>();
for (String s : List.of("Step X", "Step A", "Step N"))
    actions.put(s, () -> System.out.println("Executing action: " s));
for (int i = 0 ; i < 20; i  ) {
    final int index = i; 
    actions.put("Item " index, () -> System.out.println("Executing #" index));
}

runSomeStuff(actions);

This should print:

Running Step X...
Executing action: Step X
Running Step A...
Executing action: Step A
Running Step N...
Executing action: Step N
Running Item 0...
Executing #0
...

Note that if runSomeStuff is called with other Map implementations the order won't be the same as insertion.

CodePudding user response:

In this case, as mentioned in the comments, List is a semantically more appropriate mean of storing the data because you're not accessing values through their keys, but simply iterating over the map entries.

Instead of using a map in order to fuse the two attributes together the intuitive and maintainable way of structure the data would be to define a custom object. Since Java 16 records it might be literally done with a single line of code.

record Action(String name, Runnable task) {}

List<Action> actions = List.of(
    new Action("Step 1", () -> System.out.println(1)),
    new Action("Step 2", () -> System.out.println(2))
);

But if you're convinced that you need a Map, in case if these tasks are as simple as you've described, you can generate using a plain index-based loop or a stream.

final int actionCount = 3;
        
Map<String, CheckedRunnable> actionByStepName = IntStream.rangeClosed(1, actionCount)
    .boxed()
    .collect(Collectors.toMap(
        i -> "Step "   i,
        i -> () -> System.out.println(i),
        (left, right) -> {
            throw new AssertionError("duplicated keys are not expected");
        },
        LinkedHashMap::new
    ));
  • Related