第一题:
编写一个 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));
}
}
^-^ 你有什么好的写法欢迎在评论区留言~