簡體   English   中英

排序列表 <Map<String, String> &gt;

[英]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來存儲動作的屬性( namenextAction ), 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");
    }

    //...
}

ContinuousActionPlainAction可以進一步抽象。 它們都被命名為動作(它們具有名稱),並且該屬性以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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM