[英]sorting a List<Map<String, String>>
我有一個字符串映射列表:
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作為下一個動作的占位符可能會更容易?
問題:如何對列表進行排序,以便操作順暢? 即:動作的nextAction與列表中下一個動作的名稱相同。
不用使用Map
來存儲動作的屬性( name
和nextAction
), 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,因為它是最后一個動作,但是給它一個nextAction作為下一個動作的占位符可能會更容易
同樣的問題也適用於上面的代碼。
現在,由於構造函數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
}
}
但這將是一種代碼氣味 。 您可以在這里停止閱讀並使用簡單的修復程序,或者在下面繼續閱讀涉及更多(且更具教育意義)的路線。
當您確實使用null時,很有可能會成為代碼氣味的受害者。 盡管它不適用於所有情況(由於Java的null安全性較差),但應盡可能避免使用null 。 相反,請像本例一樣重新考慮您的設計。 如果所有其他方法均失敗,請使用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方式影響其合同 (將其傳遞給模板方法 process(String)
):
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
盡管這似乎是XY問題的一種情況,並且此地圖列表當然不是“精心設計的數據模型”,並且在許多方面都有可能是“更好”的表示形式(盡管沒有人可以提出建議)只要不知道總體目標,“最佳”模型可能是什么),這就是您手頭的任務,下面是解決方法:
首先,您必須確定排序列表的第一個元素。 這恰好是具有"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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.