Simple DFA simulator in Java

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












In an effort to learn Java, I have written a simple DFA simulator, where a DFA is emulated using an object with a collection of states and inputs. Inputs can then be passed to the DFA to cause it to switch between the states according to a transition table. I hope to expand it with functions which can execute when a Transition occurs in the future.



I'm coming mainly from C#, so tips on how to write idiomatic Java would be appreciated.



DeterministicFiniteAutomaton.java



import java.util.List;
import java.util.stream.Stream;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.HashMap;

/**
* Represents a deterministic finite automaton (DFA), with a set of states and
* transitions between those states.
*/
public class DeterministicFiniteAutomaton
private final String states;
private final String inputAlphabet;
private final String acceptingStates;

/**
* A HashMap of transitions. A HashMap is used to speed up searching
* through the table to find the correct transition.
* Keys are of the form input,currentState.
*/
private final HashMap<String, Transition> transitions = new HashMap<>();

private String startState;
private String currentState;

/**
* Constructs a new DFA.
*
* @param states The set of states which the DFA may be in.
* @param inputAlphabet The set of inputs which may be supplied to the DFA.
* @param acceptingStates The subset of states which are accepting.
* @param transitions A list of transitions between states on inputs.
* @param startState The starting state.
*/
public DeterministicFiniteAutomaton(String states, String inputAlphabet,
String acceptingStates, Transition transitions, String startState)
// Due to transition keys' comma-separated nature, state or input names
// may not contain commas

if (Stream.of(states).anyMatch(x -> x.contains(",")))
throw new InvalidParameterException
("State names may not contain commas");


if (Stream.of(inputAlphabet).anyMatch(x -> x.contains(",")))
throw new InvalidParameterException
("Inputs may not contain commas");


this.states = states;
this.inputAlphabet = inputAlphabet;
this.acceptingStates = acceptingStates;

this.startState = startState;
this.currentState = startState;

for (Transition t : transitions) !statesAsList.contains(t.startState))
throw new InvalidParameterException
("Transition refers to state which does not exist");


if (!Arrays.asList(inputAlphabet).contains(t.input))
throw new InvalidParameterException
("Transition refers to input which does not exist");


String key = getKeyForTransition(t.input, t.startState);

this.transitions.put(key, t);



/**
* Resets the DFA to its starting state.
* This method returns the object it is called on, so may be chained.
*/
public DeterministicFiniteAutomaton reset()
currentState = startState;
return this;


/**
* Given a valid input, transitions the DFA to another state according to
* the transition table.
* This method returns the object it is called on, so may be chained.
* @param input The input to the DFA.
*/
public DeterministicFiniteAutomaton input(String input)
// Check that this input is contained within the input alphabet
if (!Arrays.asList(inputAlphabet).contains(input))
throw new InvalidParameterException
("'" + input + "' is not a valid input");


String key = getKeyForTransition(input, currentState);

Transition transition = transitions.get(key);
if (transition == null)
throw new InvalidParameterException
("No transition found for: " + input);


currentState = transition.newState;

return this;


/**
* Returns the current state of the DFA.
*/
public String getState()
return currentState;


/**
* Returns true if the current state is contained within the set of
* accepting states.
*/
public boolean isAccepting()
return Arrays.asList(acceptingStates).contains(currentState);


/**
* Calculates the HashMap key used to look up the transition which should
* be taken, given the current state and an input.
*/
private String getKeyForTransition(String input, String state)
return input + "," + state;




Transition.java



/**
* Represents a transition between two states based on an input.
*/
public class Transition
public final String startState;
public final String input;
public final String newState;

/**
* Constructs a new transition.
* @param startState The state to start from.
* @param input The input on which the transition should trigger.
* @param newState The state to transition to.s
*/
public Transition(String startState, String input, String newState)
this.startState = startState;
this.input = input;
this.newState = newState;




Usage example



DeterministicFiniteAutomaton turnstileDfa =
new DeterministicFiniteAutomaton(
new String "Locked", "Unlocked" ,
new String "Push", "Coin" ,
new String "Locked" ,
new Transition
new Transition("Locked", "Push", "Locked"),
new Transition("Locked", "Coin", "Unlocked"),
new Transition("Unlocked", "Push", "Locked"),
new Transition("Unlocked", "Coin", "Unlocked")
,
"Locked"
);

turnstileDfa.input("Coin").input("Push");






share|improve this question

























    up vote
    2
    down vote

    favorite












    In an effort to learn Java, I have written a simple DFA simulator, where a DFA is emulated using an object with a collection of states and inputs. Inputs can then be passed to the DFA to cause it to switch between the states according to a transition table. I hope to expand it with functions which can execute when a Transition occurs in the future.



    I'm coming mainly from C#, so tips on how to write idiomatic Java would be appreciated.



    DeterministicFiniteAutomaton.java



    import java.util.List;
    import java.util.stream.Stream;
    import java.security.InvalidParameterException;
    import java.util.Arrays;
    import java.util.HashMap;

    /**
    * Represents a deterministic finite automaton (DFA), with a set of states and
    * transitions between those states.
    */
    public class DeterministicFiniteAutomaton
    private final String states;
    private final String inputAlphabet;
    private final String acceptingStates;

    /**
    * A HashMap of transitions. A HashMap is used to speed up searching
    * through the table to find the correct transition.
    * Keys are of the form input,currentState.
    */
    private final HashMap<String, Transition> transitions = new HashMap<>();

    private String startState;
    private String currentState;

    /**
    * Constructs a new DFA.
    *
    * @param states The set of states which the DFA may be in.
    * @param inputAlphabet The set of inputs which may be supplied to the DFA.
    * @param acceptingStates The subset of states which are accepting.
    * @param transitions A list of transitions between states on inputs.
    * @param startState The starting state.
    */
    public DeterministicFiniteAutomaton(String states, String inputAlphabet,
    String acceptingStates, Transition transitions, String startState)
    // Due to transition keys' comma-separated nature, state or input names
    // may not contain commas

    if (Stream.of(states).anyMatch(x -> x.contains(",")))
    throw new InvalidParameterException
    ("State names may not contain commas");


    if (Stream.of(inputAlphabet).anyMatch(x -> x.contains(",")))
    throw new InvalidParameterException
    ("Inputs may not contain commas");


    this.states = states;
    this.inputAlphabet = inputAlphabet;
    this.acceptingStates = acceptingStates;

    this.startState = startState;
    this.currentState = startState;

    for (Transition t : transitions) !statesAsList.contains(t.startState))
    throw new InvalidParameterException
    ("Transition refers to state which does not exist");


    if (!Arrays.asList(inputAlphabet).contains(t.input))
    throw new InvalidParameterException
    ("Transition refers to input which does not exist");


    String key = getKeyForTransition(t.input, t.startState);

    this.transitions.put(key, t);



    /**
    * Resets the DFA to its starting state.
    * This method returns the object it is called on, so may be chained.
    */
    public DeterministicFiniteAutomaton reset()
    currentState = startState;
    return this;


    /**
    * Given a valid input, transitions the DFA to another state according to
    * the transition table.
    * This method returns the object it is called on, so may be chained.
    * @param input The input to the DFA.
    */
    public DeterministicFiniteAutomaton input(String input)
    // Check that this input is contained within the input alphabet
    if (!Arrays.asList(inputAlphabet).contains(input))
    throw new InvalidParameterException
    ("'" + input + "' is not a valid input");


    String key = getKeyForTransition(input, currentState);

    Transition transition = transitions.get(key);
    if (transition == null)
    throw new InvalidParameterException
    ("No transition found for: " + input);


    currentState = transition.newState;

    return this;


    /**
    * Returns the current state of the DFA.
    */
    public String getState()
    return currentState;


    /**
    * Returns true if the current state is contained within the set of
    * accepting states.
    */
    public boolean isAccepting()
    return Arrays.asList(acceptingStates).contains(currentState);


    /**
    * Calculates the HashMap key used to look up the transition which should
    * be taken, given the current state and an input.
    */
    private String getKeyForTransition(String input, String state)
    return input + "," + state;




    Transition.java



    /**
    * Represents a transition between two states based on an input.
    */
    public class Transition
    public final String startState;
    public final String input;
    public final String newState;

    /**
    * Constructs a new transition.
    * @param startState The state to start from.
    * @param input The input on which the transition should trigger.
    * @param newState The state to transition to.s
    */
    public Transition(String startState, String input, String newState)
    this.startState = startState;
    this.input = input;
    this.newState = newState;




    Usage example



    DeterministicFiniteAutomaton turnstileDfa =
    new DeterministicFiniteAutomaton(
    new String "Locked", "Unlocked" ,
    new String "Push", "Coin" ,
    new String "Locked" ,
    new Transition
    new Transition("Locked", "Push", "Locked"),
    new Transition("Locked", "Coin", "Unlocked"),
    new Transition("Unlocked", "Push", "Locked"),
    new Transition("Unlocked", "Coin", "Unlocked")
    ,
    "Locked"
    );

    turnstileDfa.input("Coin").input("Push");






    share|improve this question





















      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      In an effort to learn Java, I have written a simple DFA simulator, where a DFA is emulated using an object with a collection of states and inputs. Inputs can then be passed to the DFA to cause it to switch between the states according to a transition table. I hope to expand it with functions which can execute when a Transition occurs in the future.



      I'm coming mainly from C#, so tips on how to write idiomatic Java would be appreciated.



      DeterministicFiniteAutomaton.java



      import java.util.List;
      import java.util.stream.Stream;
      import java.security.InvalidParameterException;
      import java.util.Arrays;
      import java.util.HashMap;

      /**
      * Represents a deterministic finite automaton (DFA), with a set of states and
      * transitions between those states.
      */
      public class DeterministicFiniteAutomaton
      private final String states;
      private final String inputAlphabet;
      private final String acceptingStates;

      /**
      * A HashMap of transitions. A HashMap is used to speed up searching
      * through the table to find the correct transition.
      * Keys are of the form input,currentState.
      */
      private final HashMap<String, Transition> transitions = new HashMap<>();

      private String startState;
      private String currentState;

      /**
      * Constructs a new DFA.
      *
      * @param states The set of states which the DFA may be in.
      * @param inputAlphabet The set of inputs which may be supplied to the DFA.
      * @param acceptingStates The subset of states which are accepting.
      * @param transitions A list of transitions between states on inputs.
      * @param startState The starting state.
      */
      public DeterministicFiniteAutomaton(String states, String inputAlphabet,
      String acceptingStates, Transition transitions, String startState)
      // Due to transition keys' comma-separated nature, state or input names
      // may not contain commas

      if (Stream.of(states).anyMatch(x -> x.contains(",")))
      throw new InvalidParameterException
      ("State names may not contain commas");


      if (Stream.of(inputAlphabet).anyMatch(x -> x.contains(",")))
      throw new InvalidParameterException
      ("Inputs may not contain commas");


      this.states = states;
      this.inputAlphabet = inputAlphabet;
      this.acceptingStates = acceptingStates;

      this.startState = startState;
      this.currentState = startState;

      for (Transition t : transitions) !statesAsList.contains(t.startState))
      throw new InvalidParameterException
      ("Transition refers to state which does not exist");


      if (!Arrays.asList(inputAlphabet).contains(t.input))
      throw new InvalidParameterException
      ("Transition refers to input which does not exist");


      String key = getKeyForTransition(t.input, t.startState);

      this.transitions.put(key, t);



      /**
      * Resets the DFA to its starting state.
      * This method returns the object it is called on, so may be chained.
      */
      public DeterministicFiniteAutomaton reset()
      currentState = startState;
      return this;


      /**
      * Given a valid input, transitions the DFA to another state according to
      * the transition table.
      * This method returns the object it is called on, so may be chained.
      * @param input The input to the DFA.
      */
      public DeterministicFiniteAutomaton input(String input)
      // Check that this input is contained within the input alphabet
      if (!Arrays.asList(inputAlphabet).contains(input))
      throw new InvalidParameterException
      ("'" + input + "' is not a valid input");


      String key = getKeyForTransition(input, currentState);

      Transition transition = transitions.get(key);
      if (transition == null)
      throw new InvalidParameterException
      ("No transition found for: " + input);


      currentState = transition.newState;

      return this;


      /**
      * Returns the current state of the DFA.
      */
      public String getState()
      return currentState;


      /**
      * Returns true if the current state is contained within the set of
      * accepting states.
      */
      public boolean isAccepting()
      return Arrays.asList(acceptingStates).contains(currentState);


      /**
      * Calculates the HashMap key used to look up the transition which should
      * be taken, given the current state and an input.
      */
      private String getKeyForTransition(String input, String state)
      return input + "," + state;




      Transition.java



      /**
      * Represents a transition between two states based on an input.
      */
      public class Transition
      public final String startState;
      public final String input;
      public final String newState;

      /**
      * Constructs a new transition.
      * @param startState The state to start from.
      * @param input The input on which the transition should trigger.
      * @param newState The state to transition to.s
      */
      public Transition(String startState, String input, String newState)
      this.startState = startState;
      this.input = input;
      this.newState = newState;




      Usage example



      DeterministicFiniteAutomaton turnstileDfa =
      new DeterministicFiniteAutomaton(
      new String "Locked", "Unlocked" ,
      new String "Push", "Coin" ,
      new String "Locked" ,
      new Transition
      new Transition("Locked", "Push", "Locked"),
      new Transition("Locked", "Coin", "Unlocked"),
      new Transition("Unlocked", "Push", "Locked"),
      new Transition("Unlocked", "Coin", "Unlocked")
      ,
      "Locked"
      );

      turnstileDfa.input("Coin").input("Push");






      share|improve this question











      In an effort to learn Java, I have written a simple DFA simulator, where a DFA is emulated using an object with a collection of states and inputs. Inputs can then be passed to the DFA to cause it to switch between the states according to a transition table. I hope to expand it with functions which can execute when a Transition occurs in the future.



      I'm coming mainly from C#, so tips on how to write idiomatic Java would be appreciated.



      DeterministicFiniteAutomaton.java



      import java.util.List;
      import java.util.stream.Stream;
      import java.security.InvalidParameterException;
      import java.util.Arrays;
      import java.util.HashMap;

      /**
      * Represents a deterministic finite automaton (DFA), with a set of states and
      * transitions between those states.
      */
      public class DeterministicFiniteAutomaton
      private final String states;
      private final String inputAlphabet;
      private final String acceptingStates;

      /**
      * A HashMap of transitions. A HashMap is used to speed up searching
      * through the table to find the correct transition.
      * Keys are of the form input,currentState.
      */
      private final HashMap<String, Transition> transitions = new HashMap<>();

      private String startState;
      private String currentState;

      /**
      * Constructs a new DFA.
      *
      * @param states The set of states which the DFA may be in.
      * @param inputAlphabet The set of inputs which may be supplied to the DFA.
      * @param acceptingStates The subset of states which are accepting.
      * @param transitions A list of transitions between states on inputs.
      * @param startState The starting state.
      */
      public DeterministicFiniteAutomaton(String states, String inputAlphabet,
      String acceptingStates, Transition transitions, String startState)
      // Due to transition keys' comma-separated nature, state or input names
      // may not contain commas

      if (Stream.of(states).anyMatch(x -> x.contains(",")))
      throw new InvalidParameterException
      ("State names may not contain commas");


      if (Stream.of(inputAlphabet).anyMatch(x -> x.contains(",")))
      throw new InvalidParameterException
      ("Inputs may not contain commas");


      this.states = states;
      this.inputAlphabet = inputAlphabet;
      this.acceptingStates = acceptingStates;

      this.startState = startState;
      this.currentState = startState;

      for (Transition t : transitions) !statesAsList.contains(t.startState))
      throw new InvalidParameterException
      ("Transition refers to state which does not exist");


      if (!Arrays.asList(inputAlphabet).contains(t.input))
      throw new InvalidParameterException
      ("Transition refers to input which does not exist");


      String key = getKeyForTransition(t.input, t.startState);

      this.transitions.put(key, t);



      /**
      * Resets the DFA to its starting state.
      * This method returns the object it is called on, so may be chained.
      */
      public DeterministicFiniteAutomaton reset()
      currentState = startState;
      return this;


      /**
      * Given a valid input, transitions the DFA to another state according to
      * the transition table.
      * This method returns the object it is called on, so may be chained.
      * @param input The input to the DFA.
      */
      public DeterministicFiniteAutomaton input(String input)
      // Check that this input is contained within the input alphabet
      if (!Arrays.asList(inputAlphabet).contains(input))
      throw new InvalidParameterException
      ("'" + input + "' is not a valid input");


      String key = getKeyForTransition(input, currentState);

      Transition transition = transitions.get(key);
      if (transition == null)
      throw new InvalidParameterException
      ("No transition found for: " + input);


      currentState = transition.newState;

      return this;


      /**
      * Returns the current state of the DFA.
      */
      public String getState()
      return currentState;


      /**
      * Returns true if the current state is contained within the set of
      * accepting states.
      */
      public boolean isAccepting()
      return Arrays.asList(acceptingStates).contains(currentState);


      /**
      * Calculates the HashMap key used to look up the transition which should
      * be taken, given the current state and an input.
      */
      private String getKeyForTransition(String input, String state)
      return input + "," + state;




      Transition.java



      /**
      * Represents a transition between two states based on an input.
      */
      public class Transition
      public final String startState;
      public final String input;
      public final String newState;

      /**
      * Constructs a new transition.
      * @param startState The state to start from.
      * @param input The input on which the transition should trigger.
      * @param newState The state to transition to.s
      */
      public Transition(String startState, String input, String newState)
      this.startState = startState;
      this.input = input;
      this.newState = newState;




      Usage example



      DeterministicFiniteAutomaton turnstileDfa =
      new DeterministicFiniteAutomaton(
      new String "Locked", "Unlocked" ,
      new String "Push", "Coin" ,
      new String "Locked" ,
      new Transition
      new Transition("Locked", "Push", "Locked"),
      new Transition("Locked", "Coin", "Unlocked"),
      new Transition("Unlocked", "Push", "Locked"),
      new Transition("Unlocked", "Coin", "Unlocked")
      ,
      "Locked"
      );

      turnstileDfa.input("Coin").input("Push");








      share|improve this question










      share|improve this question




      share|improve this question









      asked Feb 5 at 13:04









      Aaron Christiansen

      22617




      22617




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          2
          down vote



          accepted











          import java.security.InvalidParameterException;



          I'm pretty sure that you actually wanted java.lang.IllegalArgumentException here :) You do not need to explicitly import that, btw. Types from the java.lang package are automatically imported.




           * Keys are of the form input,currentState.



          That is somewhat unconventional. Especially given that you claim to implement a deterministic automaton, it would make significantly more sense to look up by state and then by input.

          That should also be accomplishable in constant time (two pointer dereferencings), and not require building a String input + "," + currentState.

          In addition it's much less prone to errors when the inputAlphabet or your states can contain the separator char. Consider:



          inputAlphabet = new String "foo,bar", "foo", "bar" ;
          states = new String "bar", "bar,bar", "foo" ;


          which is "foo,bar,bar" now? is it "foo" + "," + "bar,bar" or "foo,bar" + "," + "bar"?



          I now see that you do actually check for that in your constructor explicitly. It's good that you found the problem, the solution could use some work though :)




           private final HashMap<String, Transition> transitions = new HashMap<>();



          I really like that you've declared your fields final wherever possible.
          I also like the use of the diamond operator.



          As opposed to C#, Java interfaces are not explicitly prefixed with I. It is still preferred to use interfaces for members. Accordingly this should be:



          private final Map<String, Transition> transitions = new HashMap<>();


          Which reminds me... you should use private final Map<String, List<Transition>>, that simplifies the lookup by cutting out the key-aggregation. It additionally better reflects what a DFA really works like.



          It also allows you to use a bit more modern features in input:



          public DeterministicFiniteAutomaton input(String input) 
          // input-checks have been performed on transitions.
          // if no transition matches, the input may not have been in the alphabet
          currentState = transitions.getOrDefault(input, Collections.emptyList())
          .stream()
          .first(t -> t.input.equals(input))
          .orElseThrow(() -> new IllegalArgumentException("no transition found for: " + input))
          .newState;
          return this;



          Last but not least: I get that going to Java makes one miss C#'s Properties, but this is not an acceptable coding style in Java.



          I can warmly recommend using Project Lombok to take care of autogenerating getters (and if needed setters) for your data holder classes.






          share|improve this answer





















          • This is really helpful; thanks!
            – Aaron Christiansen
            Feb 5 at 15:56










          • I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
            – Vogel612♦
            Feb 5 at 15:59










          • Good idea; does doing that have performance benefits too?
            – Aaron Christiansen
            Feb 5 at 16:00










          • Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
            – Vogel612♦
            Feb 5 at 16:01











          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186816%2fsimple-dfa-simulator-in-java%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          2
          down vote



          accepted











          import java.security.InvalidParameterException;



          I'm pretty sure that you actually wanted java.lang.IllegalArgumentException here :) You do not need to explicitly import that, btw. Types from the java.lang package are automatically imported.




           * Keys are of the form input,currentState.



          That is somewhat unconventional. Especially given that you claim to implement a deterministic automaton, it would make significantly more sense to look up by state and then by input.

          That should also be accomplishable in constant time (two pointer dereferencings), and not require building a String input + "," + currentState.

          In addition it's much less prone to errors when the inputAlphabet or your states can contain the separator char. Consider:



          inputAlphabet = new String "foo,bar", "foo", "bar" ;
          states = new String "bar", "bar,bar", "foo" ;


          which is "foo,bar,bar" now? is it "foo" + "," + "bar,bar" or "foo,bar" + "," + "bar"?



          I now see that you do actually check for that in your constructor explicitly. It's good that you found the problem, the solution could use some work though :)




           private final HashMap<String, Transition> transitions = new HashMap<>();



          I really like that you've declared your fields final wherever possible.
          I also like the use of the diamond operator.



          As opposed to C#, Java interfaces are not explicitly prefixed with I. It is still preferred to use interfaces for members. Accordingly this should be:



          private final Map<String, Transition> transitions = new HashMap<>();


          Which reminds me... you should use private final Map<String, List<Transition>>, that simplifies the lookup by cutting out the key-aggregation. It additionally better reflects what a DFA really works like.



          It also allows you to use a bit more modern features in input:



          public DeterministicFiniteAutomaton input(String input) 
          // input-checks have been performed on transitions.
          // if no transition matches, the input may not have been in the alphabet
          currentState = transitions.getOrDefault(input, Collections.emptyList())
          .stream()
          .first(t -> t.input.equals(input))
          .orElseThrow(() -> new IllegalArgumentException("no transition found for: " + input))
          .newState;
          return this;



          Last but not least: I get that going to Java makes one miss C#'s Properties, but this is not an acceptable coding style in Java.



          I can warmly recommend using Project Lombok to take care of autogenerating getters (and if needed setters) for your data holder classes.






          share|improve this answer





















          • This is really helpful; thanks!
            – Aaron Christiansen
            Feb 5 at 15:56










          • I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
            – Vogel612♦
            Feb 5 at 15:59










          • Good idea; does doing that have performance benefits too?
            – Aaron Christiansen
            Feb 5 at 16:00










          • Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
            – Vogel612♦
            Feb 5 at 16:01















          up vote
          2
          down vote



          accepted











          import java.security.InvalidParameterException;



          I'm pretty sure that you actually wanted java.lang.IllegalArgumentException here :) You do not need to explicitly import that, btw. Types from the java.lang package are automatically imported.




           * Keys are of the form input,currentState.



          That is somewhat unconventional. Especially given that you claim to implement a deterministic automaton, it would make significantly more sense to look up by state and then by input.

          That should also be accomplishable in constant time (two pointer dereferencings), and not require building a String input + "," + currentState.

          In addition it's much less prone to errors when the inputAlphabet or your states can contain the separator char. Consider:



          inputAlphabet = new String "foo,bar", "foo", "bar" ;
          states = new String "bar", "bar,bar", "foo" ;


          which is "foo,bar,bar" now? is it "foo" + "," + "bar,bar" or "foo,bar" + "," + "bar"?



          I now see that you do actually check for that in your constructor explicitly. It's good that you found the problem, the solution could use some work though :)




           private final HashMap<String, Transition> transitions = new HashMap<>();



          I really like that you've declared your fields final wherever possible.
          I also like the use of the diamond operator.



          As opposed to C#, Java interfaces are not explicitly prefixed with I. It is still preferred to use interfaces for members. Accordingly this should be:



          private final Map<String, Transition> transitions = new HashMap<>();


          Which reminds me... you should use private final Map<String, List<Transition>>, that simplifies the lookup by cutting out the key-aggregation. It additionally better reflects what a DFA really works like.



          It also allows you to use a bit more modern features in input:



          public DeterministicFiniteAutomaton input(String input) 
          // input-checks have been performed on transitions.
          // if no transition matches, the input may not have been in the alphabet
          currentState = transitions.getOrDefault(input, Collections.emptyList())
          .stream()
          .first(t -> t.input.equals(input))
          .orElseThrow(() -> new IllegalArgumentException("no transition found for: " + input))
          .newState;
          return this;



          Last but not least: I get that going to Java makes one miss C#'s Properties, but this is not an acceptable coding style in Java.



          I can warmly recommend using Project Lombok to take care of autogenerating getters (and if needed setters) for your data holder classes.






          share|improve this answer





















          • This is really helpful; thanks!
            – Aaron Christiansen
            Feb 5 at 15:56










          • I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
            – Vogel612♦
            Feb 5 at 15:59










          • Good idea; does doing that have performance benefits too?
            – Aaron Christiansen
            Feb 5 at 16:00










          • Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
            – Vogel612♦
            Feb 5 at 16:01













          up vote
          2
          down vote



          accepted







          up vote
          2
          down vote



          accepted







          import java.security.InvalidParameterException;



          I'm pretty sure that you actually wanted java.lang.IllegalArgumentException here :) You do not need to explicitly import that, btw. Types from the java.lang package are automatically imported.




           * Keys are of the form input,currentState.



          That is somewhat unconventional. Especially given that you claim to implement a deterministic automaton, it would make significantly more sense to look up by state and then by input.

          That should also be accomplishable in constant time (two pointer dereferencings), and not require building a String input + "," + currentState.

          In addition it's much less prone to errors when the inputAlphabet or your states can contain the separator char. Consider:



          inputAlphabet = new String "foo,bar", "foo", "bar" ;
          states = new String "bar", "bar,bar", "foo" ;


          which is "foo,bar,bar" now? is it "foo" + "," + "bar,bar" or "foo,bar" + "," + "bar"?



          I now see that you do actually check for that in your constructor explicitly. It's good that you found the problem, the solution could use some work though :)




           private final HashMap<String, Transition> transitions = new HashMap<>();



          I really like that you've declared your fields final wherever possible.
          I also like the use of the diamond operator.



          As opposed to C#, Java interfaces are not explicitly prefixed with I. It is still preferred to use interfaces for members. Accordingly this should be:



          private final Map<String, Transition> transitions = new HashMap<>();


          Which reminds me... you should use private final Map<String, List<Transition>>, that simplifies the lookup by cutting out the key-aggregation. It additionally better reflects what a DFA really works like.



          It also allows you to use a bit more modern features in input:



          public DeterministicFiniteAutomaton input(String input) 
          // input-checks have been performed on transitions.
          // if no transition matches, the input may not have been in the alphabet
          currentState = transitions.getOrDefault(input, Collections.emptyList())
          .stream()
          .first(t -> t.input.equals(input))
          .orElseThrow(() -> new IllegalArgumentException("no transition found for: " + input))
          .newState;
          return this;



          Last but not least: I get that going to Java makes one miss C#'s Properties, but this is not an acceptable coding style in Java.



          I can warmly recommend using Project Lombok to take care of autogenerating getters (and if needed setters) for your data holder classes.






          share|improve this answer














          import java.security.InvalidParameterException;



          I'm pretty sure that you actually wanted java.lang.IllegalArgumentException here :) You do not need to explicitly import that, btw. Types from the java.lang package are automatically imported.




           * Keys are of the form input,currentState.



          That is somewhat unconventional. Especially given that you claim to implement a deterministic automaton, it would make significantly more sense to look up by state and then by input.

          That should also be accomplishable in constant time (two pointer dereferencings), and not require building a String input + "," + currentState.

          In addition it's much less prone to errors when the inputAlphabet or your states can contain the separator char. Consider:



          inputAlphabet = new String "foo,bar", "foo", "bar" ;
          states = new String "bar", "bar,bar", "foo" ;


          which is "foo,bar,bar" now? is it "foo" + "," + "bar,bar" or "foo,bar" + "," + "bar"?



          I now see that you do actually check for that in your constructor explicitly. It's good that you found the problem, the solution could use some work though :)




           private final HashMap<String, Transition> transitions = new HashMap<>();



          I really like that you've declared your fields final wherever possible.
          I also like the use of the diamond operator.



          As opposed to C#, Java interfaces are not explicitly prefixed with I. It is still preferred to use interfaces for members. Accordingly this should be:



          private final Map<String, Transition> transitions = new HashMap<>();


          Which reminds me... you should use private final Map<String, List<Transition>>, that simplifies the lookup by cutting out the key-aggregation. It additionally better reflects what a DFA really works like.



          It also allows you to use a bit more modern features in input:



          public DeterministicFiniteAutomaton input(String input) 
          // input-checks have been performed on transitions.
          // if no transition matches, the input may not have been in the alphabet
          currentState = transitions.getOrDefault(input, Collections.emptyList())
          .stream()
          .first(t -> t.input.equals(input))
          .orElseThrow(() -> new IllegalArgumentException("no transition found for: " + input))
          .newState;
          return this;



          Last but not least: I get that going to Java makes one miss C#'s Properties, but this is not an acceptable coding style in Java.



          I can warmly recommend using Project Lombok to take care of autogenerating getters (and if needed setters) for your data holder classes.







          share|improve this answer













          share|improve this answer



          share|improve this answer











          answered Feb 5 at 15:54









          Vogel612♦

          20.9k345124




          20.9k345124











          • This is really helpful; thanks!
            – Aaron Christiansen
            Feb 5 at 15:56










          • I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
            – Vogel612♦
            Feb 5 at 15:59










          • Good idea; does doing that have performance benefits too?
            – Aaron Christiansen
            Feb 5 at 16:00










          • Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
            – Vogel612♦
            Feb 5 at 16:01

















          • This is really helpful; thanks!
            – Aaron Christiansen
            Feb 5 at 15:56










          • I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
            – Vogel612♦
            Feb 5 at 15:59










          • Good idea; does doing that have performance benefits too?
            – Aaron Christiansen
            Feb 5 at 16:00










          • Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
            – Vogel612♦
            Feb 5 at 16:01
















          This is really helpful; thanks!
          – Aaron Christiansen
          Feb 5 at 15:56




          This is really helpful; thanks!
          – Aaron Christiansen
          Feb 5 at 15:56












          I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
          – Vogel612♦
          Feb 5 at 15:59




          I didn't yet mention that you should consider switching to Set<String> instead of String for all the fields in DFA...
          – Vogel612♦
          Feb 5 at 15:59












          Good idea; does doing that have performance benefits too?
          – Aaron Christiansen
          Feb 5 at 16:00




          Good idea; does doing that have performance benefits too?
          – Aaron Christiansen
          Feb 5 at 16:00












          Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
          – Vogel612♦
          Feb 5 at 16:01





          Yes, contains is $mathcalO(log n)$ instead of $mathcalO(n)$
          – Vogel612♦
          Feb 5 at 16:01













           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186816%2fsimple-dfa-simulator-in-java%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Chat program with C++ and SFML

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          Will my employers contract hold up in court?