【问题标题】:How to filter map and return list of values如何过滤地图并返回值列表
【发布时间】:2019-12-06 19:44:20
【问题描述】:

我正在尝试创建一个函数来过滤一个映射,该映射包含一个字符串作为键和一个飞行对象列表作为值并返回一个字符串列表。该地图表示飞行路径,任务是找到从起点到目的地的最短路径。 航班类别有两个字段,起点和目的地。因此,如果我发送纽约的维也纳,我应该得到一个包含 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))..
}

我该如何从这里开始?

【问题讨论】:

  • 如果没有这样的值,如何找到最短路径?
  • 更新了问题,希望现在更清楚了。
  • 欢迎来到 SO!最短路径需要使用队列和集合来避免循环的 BFS 之类的东西。我不确定这应该如何在一行中完成......你确定这是可能的吗?
  • 你想在一行中完成什么?最短路径的完整列表?这是不可能的。
  • 谢谢!该任务明确表示,在一行中完成它会奖励加分。所以我认为是的。

标签: java java-8 hashmap


【解决方案1】:

你可以这样做:

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

【讨论】:

  • 感谢您为此付出的努力。我看到这个问题有点不合理,绝对不值得你花时间去解决。它对我不起作用,但没关系,下面的答案确实如此。
【解决方案3】:

试试下面的代码:

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

【讨论】:

    猜你喜欢
    • 2022-12-07
    • 1970-01-01
    • 2018-10-10
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多