如何过滤映射并返回值列表

时间:2019-12-06 19:44:20

标签: java java-8 hashmap

我正在尝试创建一个函数,该函数过滤一个映射,该映射将String作为键,将Flight对象列表作为值,并返回字符串列表。该地图代表飞行路线,任务是找到从起点到目的地的最短路径。 飞行班级有两个字段,始发地和目的地。因此,如果我将邮件发送给纽约维也纳,我应该得到一个列表,其中包含fligth1和flight2。 该功能接受两个参数(字符串来源,字符串目的地)

地图中的键代表一个城市,而值则是航班到达的位置,例如:

Map<String, List<Flight>> paths = new HashMap<>();

List<Flight> flightsToBerlin = new ArrayList<>();
List<Flight> flightsToVienna = new ArrayList<>();

Flight flight1 = new Flight("Vienna", "Berlin");
Flight flight2 = new Flight("Berlin", "New York");

flightsToBerlin.add(flight1);
flightsToVienna.add(flight2);


paths.put("Vienna", flightsToVienna);
paths.put("Berlin", flightsToBerlin);

诀窍是要求将它完成在一行中。这就是让我发疯的部分。我曾尝试使用流,但在过滤地图并找到目的地后会感到困惑,就像这样:

public List<Flight> findPath(String origin, String destination) {
    return (List<Flight>) this.paths.entrySet().stream()
            .filter(x -> x.getKey().equals(destination))..
}

如何从这里继续?

3 个答案:

答案 0 :(得分:3)

您可以这样做:

return Stream.of(
        paths.values()
                .stream()
                .flatMap(Collection::stream)
                .collect(Collectors.groupingBy(Flight::getStartingLocation))
).flatMap(flights ->
        Stream.of(
                new HashMap<>(Map.of(origin, new Flight(origin, origin)))
        ).peek(back ->
                Stream.iterate(
                        List.of(origin),
                        list -> list.stream().flatMap(
                                now -> flights.getOrDefault(now, Collections.emptyList()).stream()
                                        .filter(flight -> back.putIfAbsent(flight.getDestination(), flight) == null)
                                        .map(Flight::getDestination)
                        ).collect(Collectors.toList())
                ).filter(list -> list.contains(destination)).findFirst()
        ).map(back ->
                Stream.iterate(
                        new Flight(destination, null),
                        now -> back.get(now.getStartingLocation())
                )
                        .skip(1)
                        .takeWhile(flight -> !flight.getDestination().equals(origin))
                        .collect(Collectors.toList())
        )
)
        .map(ArrayList::new)
        .peek(Collections::reverse)
        .findFirst().get();

答案 1 :(得分:2)

您要的几乎是不可能的。但是,我能够制造出一种通常能起作用的单线纸。它的主要限制是只能找到两次将您带到目的地的航班。换句话说:

Berlin   -> Vienna                       | WORKS 
New York -> Berlin   -> Vienna           | WORKS
Boston   -> New York -> Berlin -> Vienna | DOESN'T WORK

我通常花时间解释我在回答中所做的事情以及原因,但这是对Java语言的如此混乱,复杂,效率低下的可憎之处,我怀疑我能否对它做充分的解释。这是我尽力解释发生什么的尝试,但不是解释为什么发生的原因:

  1. 查找与给定的出发地或目的地相匹配的航班
  2. 检查与给定的出发地和目的地均匹配的单个航班
  3. 如果没有同时符合给定起点和终点的航班,则比较与起点或终点相匹配的每个航班,并查看是否有航班与对方以及给定参数同时对齐。示例:

             (A1)     (A2)
    findPath(Boston, Tokyo)
    
           (B1)       (B2)                      B1 aligns with A1
    Flight[Boston, Istanbul]                    C2 aligns with A2
                                                C1 aligns with B2
           (C1)      (C2)
    Flight[Istanbul, Tokyo]
    

在实施代码之前,需要进行以下导入:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream; 

我还假设您的Flight类看起来像这样:

public class Flight {

    private String origin;
    private String dest;

    public Flight(String origin, String dest) {
        this.origin = origin;
        this.dest = dest;
    }

    public String getOrigin() {
        return origin;
    }

    public String getDestination() {
        return dest;
    }

}

好的,这是您的一线飞机:

@SuppressWarnings("unchecked")
public List<Flight> findPath(String origin, String destination) {
    return new ArrayList<Flight>((Collection<Flight>) this.paths.values().stream().map(l -> l.stream().filter(f -> f.getDestination().equalsIgnoreCase(destination)|| f.getOrigin().equalsIgnoreCase(origin)).collect(Collectors.toList())).map(t -> new Object[] { t.stream().filter(f -> f.getDestination().equalsIgnoreCase(destination)&& f.getOrigin().equalsIgnoreCase(origin)).findAny(), t }).map(t -> ((Optional<Flight>) t[0]).isPresent() ? ((Optional<Flight>) t[0]).get() : t[1]).reduce((t, u) -> t instanceof Flight ? new HashSet<Flight>(Arrays.asList((Flight) t)): t instanceof HashSet ? t: u instanceof Flight ? new HashSet<Flight>(Arrays.asList((Flight) u)): u instanceof HashSet ? u: Stream.concat(((List<Flight>) t).stream().filter(f1 -> (f1.getDestination().equalsIgnoreCase(destination)&& ((List<Flight>) u).stream().anyMatch(f2 -> f1.getOrigin().equalsIgnoreCase(f2.getDestination())))|| (f1.getOrigin().equalsIgnoreCase(origin)&& ((List<Flight>) u).stream().anyMatch(f2 -> f1.getDestination().equalsIgnoreCase(f2.getOrigin())))),((List<Flight>) u).stream().filter(f1 -> (f1.getDestination().equalsIgnoreCase(destination)&& ((List<Flight>) t).stream().anyMatch(f2 -> f1.getOrigin().equalsIgnoreCase(f2.getDestination())))|| (f1.getOrigin().equalsIgnoreCase(origin)&& ((List<Flight>) t).stream().anyMatch(f2 -> f1.getDestination().equalsIgnoreCase(f2.getOrigin()))))).collect(Collectors.toList())).get());
}

答案 2 :(得分:1)

尝试以下代码:

return (List<Flight>) paths.entrySet()
                    .stream()
                    .filter(x -> x.getKey().equals(destination))
                    .map(Map.Entry::getValue)
                    .flatMap(List::stream) 
                    .collect(Collectors.toList());