2016-07-21 3 views
1

У меня есть список карты строк:сортировки 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); 

действий4 не имеет nextAction, потому что это последнее действие, но может быть проще просто дать ему nextAction, который является заполнителем для следующего действия?

Вопрос: Как я могу отсортировать список, чтобы действия были в порядке? т. Е. Следующее действие действия, такое же, как имя следующего действия в списке.

+0

Почему просто не имеют '' List , содержащий 'CreateFirstName, CreateLastName, CreateAddress, CreateEmail'? Или 'Map ', где ключ является значением 'name', а значением является' nextAction'? –

+0

oops. Это была опечатка. NextAction для CreateLastName должен быть CreateEmail. – Nadeemm

+0

Обновлено. Нет createEmailName. – Nadeemm

ответ

1

Хотя это, кажется, случай XY-Problem , и этот список карт, безусловно, не является «красиво разработанной моделью данных», и, вероятно, существует представление, которое «лучше» во многих отношениях (хотя никто не может давать рекомендации о том, что может быть «лучшей» моделью, до тех пор, пока общая цель неизвестна), это задача, которую вы имеете под рукой, и вот как это можно решить:

Прежде всего, вам необходимо определить первый элемент отсортированного списка. Это именно та карта, которая имеет запись "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; 
    } 
} 
+0

Эта реализация отлично работает для того, что я пытаюсь сделать. Благодаря! – Nadeemm

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(...))); 

Вложенные выражения могут получить довольно грязно, но мы вернемся к этому позже. Здесь есть большая проблема.

действий4 не имеет 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 
     } 
    } 

Но это было бы code smell. Вы можете прекратить чтение здесь и использовать простое исправление, или продолжить ниже для более привлекательного (и образовательного) маршрута.


Существует хорошая вероятность, что, когда вы используете нуль, вы становитесь жертвой запаха кода. Хотя это не относится ко всем случаям (из-за плохой нулевой безопасности 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 можно абстрагировать дальше. Они оба названных действия (у них есть имена), и это свойство влияет на их contract в samw пути (передавая его template methodprocess(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 
Смежные вопросы