我有一个字符串映射列表:
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
这将填充以下内容:
Map<String, String> action1 = new LinkedHashMap<>();
map.put("name", "CreateFirstName");
map.put("nextAction", "CreateLastName");
Map<String, String> action2 = new LinkedHashMap<>();
map.put("name", "CreateAddress");
map.put("nextAction", "CreateEmail");
Map<String, String> action3 = new LinkedHashMap<>();
map.put("name", "CreateLastName");
map.put("nextAction", "CreateAddress");
Map<String, String> action4 = new LinkedHashMap<>();
map.put("name", "CreateEmail");
list.add(action1);
list.add(action2);
list.add(action3);
list.add(action4);
action4没有nextAction,因为它是最后一个动作,但可能更容易为它提供一个占位符的nextAction,以便不进行下一步操作?
问题:如何对列表进行排序,以便按顺序执行操作? ie:动作的nextAction,与列表中下一个动作的名称相同。
答案 0 :(得分:1)
不是使用Map
来存储操作的属性(name
和nextAction
),而是创建由这些属性组成的自己的类型:
class Action {
private String name;
//nextAction
public void perform() {
//do current action
//use nextAction to perform the next action
}
}
nextAction
现在可以作为下一个操作的引用:
abstract class Action implements Action {
private String name;
private Action nextAction;
public Action(String name) {
this.name = name;
}
public final void perform() {
perform(name);
nextAction.perform();
}
protected abstract void perform(String name);
}
您现在可以通过对Action
类进行子类型化来创建操作:
class CreateFirstName extends Action {
public CreateFirstName(Action nextAction) {
super("CreateFirstName", nextAction);
}
protected final void perform(String name) {
System.out.println("Performing " + name);
}
}
将它们连在一起:
Action action = new CreateFirstName(new CreateLastName(new CreateEmail(...)));
嵌套表达式会变得非常混乱,但我们稍后会讨论。这里有一个更大的问题。
action4没有nextAction,因为它是最后一个动作,但可能更容易为它提供一个nextForction,它是一个占位符,没有下一个动作
同样的问题适用于上面的代码。
现在,由于构造函数Action(String, Action)
,每个操作必须有下一个操作。我们可以采用简单的路线并传入一个占位符,无需下一步操作(null
是最简单的路线):
class End extends Action {
public End() {
super("", null);
}
}
并进行空检查:
//class Action
public void perform() {
perform(name);
if(nextAction != null) {
nextAction.perform(); //performs next action
}
}
但这可能是code smell。你可以在这里停止阅读并使用简单的修复程序,或者在下面继续阅读更多参与(和教育)的路线。
当你使用null时,很可能会成为代码气味的牺牲品。虽然它不适用于所有情况(由于Java的空安全性差),但您应该尝试avoid null if possible。相反,请重新考虑您的设计,如本例所示。如果所有其他方法都失败了,请使用Optional
。
最后一个动作与其他动作不同。它仍然可以像其他一样执行,但它有不同的属性要求。
这意味着它们可以共享相同的行为抽象,但在定义属性时必须有所不同:
interface Action {
void perform();
}
abstract class ContinuousAction implements Action {
private String name;
private Action nextAction;
public ContinuousAction(String name) {
this.name = name;
}
public final void perform() {
perform(name);
nextAction.perform();
}
protected abstract void perform(String name);
}
abstract class PlainAction implements Action {
private String name;
public PlainAction(String name) {
this.name = name;
}
public final void perform() {
perform(name);
}
protected abstract void perform(String name);
}
最后一项操作会延长PlainAction
,而其他操作会延长ContinuousAction
。
最后,为了防止长链:
new First(new Second(new Third(new Fourth(new Fifth(new Sixth(new Seventh(new Eighth(new Ninth(new Tenth())))))))))
您可以在每个具体操作中指定下一个操作:
class CreateFirstName extends ContinuousAction {
public CreateFirstName() {
super("CreateFirstName", new CreateLastName());
}
//...
}
class CreateLastName extends ContinuousAction {
public CreateLastName() {
super("CreateLastName", new CreateEmail());
}
//...
}
class CreateEmail extends PlainAction {
public CreateEmail() {
super("CreateEmail");
}
//...
}
可以进一步抽象ContinuousAction
和PlainAction
。它们都是命名的操作(它们具有名称),并且该属性以samw方式影响其contract(将其传递给template method process(String)
):< / p>
abstract class NamedAction implements Action {
private String name;
public NamedAction(String name) {
this.name = name;
}
public final void perform() {
perform(name);
}
protected abstract void perform(String name);
}
//class ContinuousAction extends NamedAction
//class PlainAction extends NamedAction
答案 1 :(得分:1)
虽然这似乎是XY-Problem的情况,但这个地图列表当然不是一个设计良好的数据模型&#34;,并且可能有一个表示&#34;更好&#34;在很多方面(虽然没有人可以提出关于&#34;最佳&#34;模型的建议,只要总体目标未知),这就是你手边的任务,这就是它的方法可以解决:
首先,您必须确定排序列表的第一个元素。这正是具有"name"
条目的地图,该条目不会显示为任何其他地图的"nextAction"
条目。
拥有第一张地图后,您可以将其添加到(已排序)列表中。然后,确定下一个元素归结为查找"name"
与前一个地图的"nextAction"
相同的地图。要快速找到这些后继者,您可以构建一个映射,将每个"name"
条目映射到映射本身。
以下是此排序方法的基本实现:
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SortListWithMaps
{
public static void main(String[] args)
{
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> action1 = new LinkedHashMap<>();
action1.put("name", "CreateFirstName");
action1.put("nextAction", "CreateLastName");
Map<String, String> action2 = new LinkedHashMap<>();
action2.put("name", "CreateAddress");
action2.put("nextAction", "CreateEmail");
Map<String, String> action3 = new LinkedHashMap<>();
action3.put("name", "CreateLastName");
action3.put("nextAction", "CreateAddress");
Map<String, String> action4 = new LinkedHashMap<>();
action4.put("name", "CreateEmail");
list.add(action1);
list.add(action2);
list.add(action3);
list.add(action4);
// Make it a bit more interesting...
Collections.shuffle(list);
System.out.println("Before sorting");
for (Map<String, String> map : list)
{
System.out.println(map);
}
List<Map<String, String>> sortedList = sort(list);
System.out.println("After sorting");
for (Map<String, String> map : sortedList)
{
System.out.println(map);
}
}
private static List<Map<String, String>> sort(
List<Map<String, String>> list)
{
// Compute a map from "name" to the actual map
Map<String, Map<String, String>> nameToMap =
new LinkedHashMap<String, Map<String,String>>();
for (Map<String, String> map : list)
{
String name = map.get("name");
nameToMap.put(name, map);
}
// Determine the first element for the sorted list. For that,
// create the set of all names, and remove all of them that
// appear as the "nextAction" of another entry
Set<String> names =
new LinkedHashSet<String>(nameToMap.keySet());
for (Map<String, String> map : list)
{
String nextAction = map.get("nextAction");
names.remove(nextAction);
}
if (names.size() != 1)
{
System.out.println("Multiple possible first elements: " + names);
return null;
}
// Insert the elements, in sorted order, into the result list
List<Map<String, String>> result =
new ArrayList<Map<String, String>>();
String currentName = names.iterator().next();
while (currentName != null)
{
Map<String, String> element = nameToMap.get(currentName);
result.add(element);
currentName = element.get("nextAction");
}
return result;
}
}