美团点评技术部基础业务研发笔试题

第一题:

编写一个 java 函数,实现获取 1000 个用户昵称的能力,具体要求如下:
1)提供一个函数 Service.get(List<Long> userIds),支持传入 1000 个用户 ID,返回用户 ID 及其对应的昵称
2)在 Service.get 内部,通过调用 UserService.getUserMap(List<Long> userIds)获取数据,但 UserService.getUserMap 每次最多只接收 50 个用户 ID,且该接口一次请求耗时100ms
3)Service.get(List<Long> userIds)函数需要将所有 1000 个传入的用户 ID 一次返回,并且耗时需要保证在 200ms 以内。
static class UserService{
    public Map<Long, String> getUserMap(List<Long> userIds){
        if(userIds == null || userIds.size() > 50){
            throw  new RuntimeException("userids more than 50");
        }
        // 模拟耗时100ms
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 返回用户昵称
        Map<Long, String> result = new HashMap();
        for(Long userId : userIds){
            result.put(userId, "test");
        }
        return result;
    }

}
static class Service{
        private UserService userService;

        public Service(UserService userService) {
            this.userService = userService;
        }
        public Map<Long, String> get(List<Long> userIds) throws InterruptedException, ExecutionException {
            // 创建线程池
            ExecutorService executor = Executors.newFixedThreadPool(20);
            List<Callable<Map<Long, String>>> tasks = new ArrayList<>();

            // 将所有用户 ID 分成小批次,每次最多传入 50 个用户 ID
            for (int i = 0; i < userIds.size(); i += 50) {
                final List<Long> subUserIds = userIds.subList(i, Math.min(i + 50, userIds.size()));
                tasks.add(() -> userService.getUserMap(subUserIds));
            }

            // 并发调用 UserService.getUserMap 方法
            List<Future<Map<Long, String>>> futures = executor.invokeAll(tasks);

            // 合并结果
            Map<Long, String> resultMap = new HashMap<>();
            for (Future<Map<Long, String>> future : futures) {
                // 将每个小批次获取到的结果合并到总结果中
                resultMap.putAll(future.get());
            }
            // 关闭线程池
            executor.shutdown();
            return resultMap;
        }
}


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        UserService userService = new UserService();
        Service service = new Service(userService);

        List<Long> userIds = new ArrayList<>();
        for (long i = 1; i <= 1000; i++) {
            userIds.add(i);
        }

        long startTime = System.currentTimeMillis();
        Map<Long, String> resultMap = service.get(userIds);
        long endTime = System.currentTimeMillis();

        // 打印结果及耗时
        System.out.println("耗时:" + (endTime - startTime) + "ms");
        for (Map.Entry<Long, String> entry : resultMap.entrySet()) {
            System.out.println("用户ID:" + entry.getKey() + ",昵称:" + entry.getValue());
        }
    }

第二题:

设计一个 Java 缓存中间件系统,该中间件的缓存写入后,经过指定时间缓存要自动失效。
在读取缓存时,如果缓存不存在,需要异步调用 AService.get(K) 接口获取实时数据更新到缓存中。
 interface AService {
        <K, V> V get(K key);
    }
    static class CacheMiddleware<K, V> {
            private Map<K, V> cacheMap;
            private Map<K, Future<V>> futureMap;
            private ExecutorService executorService;
            private AService aService;
            private long expirationTime; // 缓存过期时间,单位:毫秒
            public CacheMiddleware(AService aService, long expirationTime) {
                this.cacheMap = new HashMap<>();
                this.futureMap = new HashMap<>();
                this.executorService = Executors.newFixedThreadPool(10);
                this.aService = aService;
                this.expirationTime = expirationTime;
            }
            public V get(K key) {
                V value = cacheMap.get(key);
                if (value == null) {
                    Future<V> future = futureMap.get(key);
                    if (future == null) {
                        // 缓存不存在,异步调用AService接口获取数据
                        future = executorService.submit(() -> aService.get(key));
                        futureMap.put(key, future);
                    }
                    try {
                        value = future.get(); // 等待AService接口返回数据
                        cacheMap.put(key, value); // 更新缓存
                        scheduleExpiration(key); // 定时过期
                        futureMap.remove(key); // 移除Future
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
                return value;
            }
            public void put(K key, V value) {
                cacheMap.put(key, value);
                scheduleExpiration(key);
            }
            public void remove(K key) {
                cacheMap.remove(key);
            }
            public void update(K key) {
                remove(key); // 移除缓存,后续调用get方法会重新获取数据
            }
            private void scheduleExpiration(K key) {
                ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
                scheduler.schedule(() -> cacheMap.remove(key), expirationTime, TimeUnit.MILLISECONDS);
                scheduler.shutdown();
            }
    }

    static class MyAService implements AService {
        @Override
        public <K, V> V get(K key) {
            // 这里可以是从数据库、远程接口等地方获取数据的具体逻辑
            // 这里简单返回一个模拟数据
            return (V) ("Data for key: " + key);
        }
    }

        public static void main(String[] args) {
            // 创建 AService 实例
            AService aService = new MyAService();

            // 创建 CacheMiddleware 实例,设置缓存过期时间为 5 秒
            CacheMiddleware<String, String> cacheMiddleware = new CacheMiddleware<>(aService, 5000);

            // 测试缓存中间件的使用
            String key = "example_key";

            // 第一次获取数据,此时缓存为空,会调用 AService 接口获取数据,并缓存起来
            System.out.println("First get: " + cacheMiddleware.get(key));

            // 第二次获取数据,此时缓存中已经有数据了,直接从缓存中获取
            System.out.println("Second get: " + cacheMiddleware.get(key));

            // 等待缓存过期
            try {
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 缓存过期后,再次获取数据,会重新调用 AService 接口获取数据,并更新缓存
            System.out.println("Third get (after expiration): " + cacheMiddleware.get(key));
        }

第三题:

设计一个用户头像展示器,在 A 页面展示尺寸为 1*1 的头像,在 B 页面展示尺寸为 2*2 的头像。
1)已有函数 UserService.getUserPicWithTargetSize(int height, int width),可以返回尺寸为 height * width 的头像 url;
2)提供一个函数 UserService.getUserPicForPage(int pageSource),返回页面对应尺寸的头像 url;
3)不能用 if-else 、三元表达式等条件控制语法来实现这个函数。
static class UserService {
        public static final int PAGE_SOURCE_A = 1;
        public static final int PAGE_SOURCE_B = 2;
        /**
         * 返回尺寸为 height * width 的头像 url
         * 可直接使用
         */
        private static String getUserPicWithTargetSize(int height, int width){
            return String.format("http://userpic_mock_h_%d_w_%d", height, width);
        }
        /**
         * 返回页面对应尺寸的头像 url
         */
        private String getUserPicForPage(int pageSource){
            Map<Integer, Function<String, String>> pageSourceMap = new HashMap<>();
            pageSourceMap.put(PAGE_SOURCE_A, size -> getUserPicWithTargetSize(1, 1));
            pageSourceMap.put(PAGE_SOURCE_B, size -> getUserPicWithTargetSize(2, 2));

            Function<String, String> function = pageSourceMap.getOrDefault(pageSource, size -> null);
            return function.apply("");
        }
        public static void main(String[] args) {
            UserService userService = new UserService();
            System.out.println(userService.getUserPicForPage(UserService.PAGE_SOURCE_A));
            System.out.println(userService.getUserPicForPage(UserService.PAGE_SOURCE_B));
        }
    }

^-^ 你有什么好的写法欢迎在评论区留言~

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值