mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-01 12:24:14 +01:00
CommandHistory optimization
- Use LinkedList with ListIterator to make all methods except for `clear()` run in `O(1)` (constant runtime) instead of `O(n)` (linear runtime). - No longer store executed commands that are executed multiple times (executing {1, 1, 1, 1, 2} now only adds {1, 2} to the history).
This commit is contained in:
parent
f5b383113f
commit
1d21f0cca3
@ -1,7 +1,7 @@
|
|||||||
package processing.app;
|
package processing.app;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of command history in console-like applications.
|
* Keeps track of command history in console-like applications.
|
||||||
@ -9,9 +9,10 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class CommandHistory {
|
public class CommandHistory {
|
||||||
|
|
||||||
private List<String> commandHistory = new ArrayList<String>();
|
private final LinkedList<String> commandHistory = new LinkedList<String>();
|
||||||
private int selectedCommandIndex = 0;
|
|
||||||
private final int maxHistorySize;
|
private final int maxHistorySize;
|
||||||
|
private ListIterator<String> iterator = null;
|
||||||
|
private boolean iteratorAsc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link CommandHistory}.
|
* Create a new {@link CommandHistory}.
|
||||||
@ -19,26 +20,41 @@ public class CommandHistory {
|
|||||||
*/
|
*/
|
||||||
public CommandHistory(int maxHistorySize) {
|
public CommandHistory(int maxHistorySize) {
|
||||||
this.maxHistorySize = (maxHistorySize < 0 ? 0 : maxHistorySize);
|
this.maxHistorySize = (maxHistorySize < 0 ? 0 : maxHistorySize);
|
||||||
this.commandHistory.add(""); // Current command placeholder.
|
this.commandHistory.addLast(""); // Current command placeholder.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given command to the history and resets the history traversal
|
* Adds the given command to the history and resets the history traversal
|
||||||
* position to the latest command. If the max history size is exceeded,
|
* position to the latest command. If the latest command in the history is
|
||||||
* the oldest command will be removed from the history.
|
* equal to the given command, it will not be added to the history.
|
||||||
|
* If the max history size is exceeded, the oldest command will be removed
|
||||||
|
* from the history.
|
||||||
* @param command - The command to add.
|
* @param command - The command to add.
|
||||||
*/
|
*/
|
||||||
public void addCommand(String command) {
|
public void addCommand(String command) {
|
||||||
|
if (this.maxHistorySize == 0) {
|
||||||
// Remove the oldest command if the max history size is exceeded.
|
return;
|
||||||
if(this.commandHistory.size() >= this.maxHistorySize + 1) {
|
|
||||||
this.commandHistory.remove(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new command, reset the 'current' command and reset the index.
|
// Remove 'current' command.
|
||||||
this.commandHistory.set(this.commandHistory.size() - 1, command);
|
this.commandHistory.removeLast();
|
||||||
this.commandHistory.add(""); // Current command placeholder.
|
|
||||||
this.selectedCommandIndex = this.commandHistory.size() - 1;
|
// Add new command if it differs from the latest command.
|
||||||
|
if (this.commandHistory.isEmpty()
|
||||||
|
|| !this.commandHistory.getLast().equals(command)) {
|
||||||
|
|
||||||
|
// Remove oldest command if max history size is exceeded.
|
||||||
|
if (this.commandHistory.size() >= this.maxHistorySize) {
|
||||||
|
this.commandHistory.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new command and reset 'current' command.
|
||||||
|
this.commandHistory.addLast(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-add 'current' command and reset command iterator.
|
||||||
|
this.commandHistory.addLast(""); // Current command placeholder.
|
||||||
|
this.iterator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +63,14 @@ public class CommandHistory {
|
|||||||
* returns {@code false} otherwise.
|
* returns {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasNextCommand() {
|
public boolean hasNextCommand() {
|
||||||
return this.selectedCommandIndex + 1 < this.commandHistory.size();
|
if (this.iterator == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.iteratorAsc) {
|
||||||
|
this.iterator.next(); // Current command, ascending.
|
||||||
|
this.iteratorAsc = true;
|
||||||
|
}
|
||||||
|
return this.iterator.hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,8 +78,20 @@ public class CommandHistory {
|
|||||||
* @return The next command or {@code null} if no next command is available.
|
* @return The next command or {@code null} if no next command is available.
|
||||||
*/
|
*/
|
||||||
public String getNextCommand() {
|
public String getNextCommand() {
|
||||||
return this.hasNextCommand()
|
|
||||||
? this.commandHistory.get(++this.selectedCommandIndex) : null;
|
// Return null if there is no next command available.
|
||||||
|
if (!this.hasNextCommand()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get next command.
|
||||||
|
String next = this.iterator.next();
|
||||||
|
|
||||||
|
// Reset 'current' command when at the end of the list.
|
||||||
|
if (this.iterator.nextIndex() == this.commandHistory.size()) {
|
||||||
|
this.iterator.set(""); // Reset 'current' command.
|
||||||
|
}
|
||||||
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,15 +100,22 @@ public class CommandHistory {
|
|||||||
* returns {@code false} otherwise.
|
* returns {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasPreviousCommand() {
|
public boolean hasPreviousCommand() {
|
||||||
return this.selectedCommandIndex > 0;
|
if (this.iterator == null) {
|
||||||
|
return this.commandHistory.size() > 1;
|
||||||
|
}
|
||||||
|
if (this.iteratorAsc) {
|
||||||
|
this.iterator.previous(); // Current command, descending.
|
||||||
|
this.iteratorAsc = false;
|
||||||
|
}
|
||||||
|
return this.iterator.hasPrevious();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the previous (older) command from the history.
|
* Gets the previous (older) command from the history.
|
||||||
* When this method is called while the most recent command in the history is
|
* When this method is called while the most recent command in the history is
|
||||||
* selected, this will store the current command as temporary latest command
|
* selected, this will store the current command as temporary latest command
|
||||||
* so that {@link #getNextCommand()} will return it. This temporary latest
|
* so that {@link #getNextCommand()} will return it once. This temporary
|
||||||
* command gets reset when this case occurs again or when
|
* latest command gets reset when this case occurs again or when
|
||||||
* {@link #addCommand(String)} is invoked.
|
* {@link #addCommand(String)} is invoked.
|
||||||
* @param currentCommand - The current unexecuted command.
|
* @param currentCommand - The current unexecuted command.
|
||||||
* @return The previous command or {@code null} if no previous command is
|
* @return The previous command or {@code null} if no previous command is
|
||||||
@ -86,14 +128,21 @@ public class CommandHistory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store current unexecuted command if not traversing already.
|
// Store current unexecuted command and create iterator if not traversing.
|
||||||
if (this.selectedCommandIndex == this.commandHistory.size() - 1) {
|
if (this.iterator == null) {
|
||||||
this.commandHistory.set(this.commandHistory.size() - 1,
|
this.iterator =
|
||||||
(currentCommand == null ? "" : currentCommand));
|
this.commandHistory.listIterator(this.commandHistory.size());
|
||||||
|
this.iterator.previous(); // Last element, descending.
|
||||||
|
this.iteratorAsc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current unexecuted command if on 'current' index.
|
||||||
|
if (this.iterator.nextIndex() == this.commandHistory.size() - 1) {
|
||||||
|
this.iterator.set(currentCommand == null ? "" : currentCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the previous command.
|
// Return the previous command.
|
||||||
return this.commandHistory.get(--this.selectedCommandIndex);
|
return this.iterator.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +152,7 @@ public class CommandHistory {
|
|||||||
* was set.
|
* was set.
|
||||||
*/
|
*/
|
||||||
public String resetHistoryLocation() {
|
public String resetHistoryLocation() {
|
||||||
this.selectedCommandIndex = this.commandHistory.size() - 1;
|
this.iterator = null;
|
||||||
return this.commandHistory.set(this.commandHistory.size() - 1, "");
|
return this.commandHistory.set(this.commandHistory.size() - 1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,8 +160,8 @@ public class CommandHistory {
|
|||||||
* Clears the command history.
|
* Clears the command history.
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
this.iterator = null;
|
||||||
this.commandHistory.clear();
|
this.commandHistory.clear();
|
||||||
this.commandHistory.add(""); // Current command placeholder.
|
this.commandHistory.addLast(""); // Current command placeholder.
|
||||||
this.selectedCommandIndex = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user