一道有意思的面试题:策略模式、Comparator可变顺多条件查询

641 阅读3分钟

题干

假设有一外卖app,发现附近有5个店铺,店铺信息包括ld、评价得分(score) 、人均消费额(average) 、以及距离(distance) ,具体数据如下:
private static final List<Shop> shops = new ArrayList<Shop>() {{
    add(new Shop(1, 9, 20.8, 5.6));
    add(new Shop(2, 8, 14.0, 2.3));
    add(new Shop(3, 4, 60.9, 8.1));
    add(new Shop(4, 8, 20.8, 3.6));
    add(new Shop(5, 6, 20.8, 3.6));
}};
请你使用java Comparator对这些商家进行内存排序,要求合理运用设计模式达到任意组合这三个维度及其升降序的目的(参考sql的多条件排序) ,入参为字符串,格式示例:sort=average&order=desc,sort=score&order=asc,sort=distance&order=asc。
允许输入一组或多组,多组排序条件用逗号隔开,sort指以哪个字段排序, order指升序还是降序,执行后输出店铺id的排列,并以中划线连接,如输入sort= score&order=desc,sort= average&order=asc,则输出1-2-4-5-3

查找资料与思考

定义一系列的算法,把他们一个个封装起来,在使用他们的时候可以相互替换,并且不会影响到使用算法的客户端。

简单来说就是我们使用Collections.sort时就是将可能会变动的排序算法编入了不变的接口中,我们可以根据需求改动接口的内容,外界依然可以正常调用,同时也可以给不同类型数据提供服务。类似的,Comparator接口也是如此,简单思考应该能明白,也可以再找一些设计模式的博客或书籍补充一下相关知识(别像我一样凉掉)。

整体思路

  1. 根据需求创建自定义Comparator实现接口,既然有多个排序需求,自然要分别定义多个Comparator,有朋友和我交流时认为可以直接整合,但整合多条件判断会导致无法自由改变条件顺序,若有更好思路,网友可留下评论讨论。

    static class ScoreAscComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return o1.score - o2.score;
        }
    }
    
  2. 将输入字符串切分,转化为自定义数组,创建Comparator列表依次add自定义Comparator

    String[] parameter = input.split(",");
    List<Comparator<Shop>> comparators = new ArrayList<>();
    for (String s : parameter) {
        s = s.replaceAll("sort=|&order", "");
        comparators.add(comparatorMap.get(s));
    }
    
  3. 对shops队列进行排序,return 0的位置蛮妙的,忘记哪篇博客学习到的这个技巧,远程向那位大佬致意了

    shopsCopy.sort((o1, o2) -&gt; {
        for (Comparator&lt;Shop&gt; comparator : comparators) {
            if (comparator.compare(o1, o2) &lt; 0) {
                return -1;
            } else if (comparator.compare(o1, o2) &gt; 0) {
                return 1;
            }
        }
        return 0;
    });gg
    
  4. 根据要求输出,这就没什么好说的了

    List<String> output = new ArrayList<>();
    for (Shop shop : shopsCopy) {
        output.add(String.valueOf(shop.getId()));
    }
    System.out.println(StringUtils.join(output, "-"));
    

完整代码

public class Main {
    static class Shop {
        private int id;
        private int score;
        private double average;
        private double distance;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Shop() {
        }

        public Shop(int id, int score, double average, double distance) {
            this.id = id;
            this.score = score;
            this.average = average;
            this.distance = distance;
        }
    }

    static class ScoreAscComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return o1.score - o2.score;
        }
    }

    static class ScoreDescComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return o2.score - o1.score;
        }
    }

    static class AverageAscComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return (int) (o1.average - o2.average);
        }
    }

    static class AverageDescComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return (int) (o2.average - o1.average);
        }
    }

    static class DistanceAscComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return (int) (o1.distance - o2.distance);
        }
    }

    static class DistanceDescComparator implements Comparator<Shop> {
        @Override
        public int compare(Shop o1, Shop o2) {
            return (int) (o2.distance - o1.distance);
        }
    }

    private static final Map<String, Comparator<Shop>> comparatorMap = new HashMap<String, Comparator<Shop>>() {{
        put("score=asc", new ScoreAscComparator());
        put("score=desc", new ScoreDescComparator());
        put("average=asc", new AverageAscComparator());
        put("average=desc", new AverageDescComparator());
        put("distance=asc", new DistanceAscComparator());
        put("distance=desc", new DistanceDescComparator());
    }};

    private static final List<Shop> shops = new ArrayList<Shop>() {{
        add(new Shop(1, 9, 20.8, 5.6));
        add(new Shop(2, 8, 14.0, 2.3));
        add(new Shop(3, 4, 60.9, 8.1));
        add(new Shop(4, 8, 20.8, 3.6));
        add(new Shop(5, 6, 20.8, 3.6));
    }};

    public static void main(String[] args) {
        //输入值例:sort=average&order=desc,sort=score&order=asc,sort=distance&order=asc
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();

        //输入值处理为类键值对格式:score=asc
        String[] parameter = input.split(",");
        List<Comparator<Shop>> comparators = new ArrayList<>();
        for (String s : parameter) {
            s = s.replaceAll("sort=|&order", "");
            comparators.add(comparatorMap.get(s));
        }

        List<Shop> shopsCopy = new ArrayList<>(Arrays.asList(new Shop[shops.size()]));
        Collections.copy(shopsCopy, shops);

        shopsCopy.sort((o1, o2) -> {
            for (Comparator<Shop> comparator : comparators) {
                if (comparator.compare(o1, o2) < 0) {
                    return -1;
                } else if (comparator.compare(o1, o2) > 0) {
                    return 1;
                }
            }
            return 0;
        });

        List<String> output = new ArrayList<>();
        for (Shop shop : shopsCopy) {
            output.add(String.valueOf(shop.getId()));
        }
        System.out.println(StringUtils.join(output, "-"));
    }
}