从写完到写好的思维革命:让代码提前结束

27 阅读3分钟

题目一:识别一个Map<String, String>中前缀为header_的key是否超过10个,你会怎么写?

我想你会这么写:

public boolean checkFeatures(Map<String, String> features) {
    if (MapUtils.isEmpty(features)) {
        return false;
    }
    int count = 0;
    for (Map.Entry<String, String> feature : features.entrySet()) {
        String key = feature.getKey();
        if (StringUtils.startsWith(key, "header_")) {
            count++;
        }
    }
    return count > 10;
}

代码写完了,看起来没毛病,但给你3秒思考,是否可以优化。 。 。?

试想,在遍历Map的过程中,如果已经找到了第11个前缀为“header_”的key,那不就可以提前结束了吗?

所以可以这样改:

public boolean checkFeatures(Map<String, String> features) {
    if (MapUtils.isEmpty(features)) {
        return false;
    }
    int count = 0;
    for (Map.Entry<String, String> feature : features.entrySet()) {
        String key = feature.getKey();
        if (StringUtils.startsWith(key, "header_")) {
            count++;
            if (count > 10) {
                // 提前结束
                return true;
            }
        }
    }
    return false;
}

看起来好了些,但再给你3秒思考,是否还可以优化。 。 。?

试想,如果features中entry个数都没有超过10个,那这个Map还需要遍历吗?

所以可以这样改:

public boolean checkFeatures(Map<String, String> features) {
    if (MapUtils.isEmpty(features)) {
        return false;
    }
    if (features.size() < 10) {
        // 提前结束
        return false;
    }
    int count = 0;
    for (Map.Entry<String, String> feature : features.entrySet()) {
        String key = feature.getKey();
        if (StringUtils.startsWith(key, "header_")) {
            count++;
            if (count > 10) {
                // 提前结束
                return true;
            }
        }
    }
    return false;
}

题目二:普通的翻页查询,你会怎么写?

先看一版普通的实现

public PageResult<User> queryUsers(String name, int page, int size) {
    if (page < 1) page = 1;
    if (size < 1) size = 10;

    int offset = (page - 1) * size;

    List<User> data = userDao.queryUsersByPage(name, size, offset);
    long total = userDao.countUsers(name);

    return new PageResult<>(data, total, page, size);
}

中规中矩,一般都是这么写,可能有的人是借助框架实现,但背后的逻辑其实就是两条sql。

给你3秒思考,是否还可以优化。 。 。?

public PageResult<User> queryUsers(String name, int page, int size) {
    if (page < 1) page = 1;
    if (size < 1) size = 10;

    int offset = (page - 1) * size;

    long total = userDao.countUsers(name);
    // 如果页码翻过了则不需要查询当前页的内容了
    if (offset + 1 > total) {
        return new PageResult<>(new ArrayList<>(), total, page, size);
    }

    List<User> data = userDao.queryUsersByPage(name, size, offset);
    return new PageResult<>(data, total, page, size);
}

其实这两个查询之间有一定的关系,如果先查询出总数,那么就知道现在翻页是否已经翻过最后一页了,如果翻过

了,就不再查询当前页的具体数据了,减少了一次数据库查询(至于说这种情况是否存在,真正的互联网没有什么不存在的,比如:前端bug、黑客红客扫描等)

题目三:从由26个小写字母组成的字符串中找出现次数最多的字母(若有多个,则任何任意一个即可),你会怎么写?

这个问题看起来也很简单,大部分人会使用一个数组来记录每个字母出现的次数,每个字母的下标是x-'a'得到,数组元素值是出现的次数,然后遍历这个数组找出最大的即可,但这个也可以提前结束,大家想一想。。。

总结

这几段代码未使用任何算法,普通小白都能看懂,但现在市面上的程序员没有多少人会很自然的写出这些简单的优化点。如果你能第一时间就意识到这些优化点,那你的思维已经明显超过一般程序员了,做到了从“写完”到“写好”的思维跃迁。

查询类代码,尽量提前结束,可在部分场景大幅提升系统的性能,降低系统开销,这些优化不是必须的,但这种思考应该有。