LeetCode 355 design-twitter

LeetCode 355 design-twitter

题目
355.设计推特

设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近十条推文。你的设计需要支持以下的几个功能:

postTweet(userId, tweetId): 创建一条新的推文
getNewsFeed(userId): 检索最近的十条推文。每个推文都必须是由此用户关注的人或者是用户自己发出的。推文必须按照时间顺序由最近的开始排序。
follow(followerId, followeeId): 关注一个用户
unfollow(followerId, followeeId): 取消关注一个用户

全文目录

  1. 设计思路
    1.1 数据结构设计
    1.2 业务逻辑的处理
    1.3 其他代码
  2. 测试验证
    2.1 硬编码测试用例
    2.1.1 运行推特
    2.1.2 使用用例测试推特
    2.2 从文件中加载测试用例
    2.3 数据结构转换的代码
  3. 完整题解代码
  4. 完整的启动推特和测试推特代码
    4.1 启动推特
    4.2 加载用例
    4.3 测试推特附录

[TOC]

04.15 更新说明
之前采用 ArrayList<TwitterUser> 结构存储用户,采用 LinkedList<Integer> 存储用户的关注列表和粉丝列表
这种结构有缺陷:
每次查找用户的时间复杂度为O(N), N为总用户数;
在拉取推文信息流时,判断每个推文是否符合要求,都需要遍历"我关注的人"列表,时间复杂度为O(m), M为我关注的人数
本次相应地改为 HashMap<Integer, TwitterUser>HashSet<Integer>,以降低这部分时间消耗

1. 设计思路

1.1 数据结构设计

  1. 自定义数据结构,用户表达推文
public class Tweet {
    private Integer id;
    private Integer userId;
    // private Date postTime;  // 实际上可以用整数替代,而本解法中,推文按照时间顺序排序,也并不需要记录发文时间
    
    // 省略构造函数与getter/setter
}
  1. 自定义数据结构,用于表达用户的关注、被关注关系
    其中,一个列表记录当前用户关注的人ID;一个列表记录 当前用户被关注人的ID
    *注意,用户之间的关注关系会频繁变动,需尽量保证查找和删除的时间复杂度最低,所以会采用 HashSet结构
public class TwitterUser {
    private Integer id;
    private Set<Integer> fans;  // 关注我的人
    private Set<Integer> follower;  // 我关注的人

    // 省略构造函数与getter/setter
}
  1. 存储全部推文、用户关系
    为了便于取时间最近的10条推文,我们考虑将推文按发文时间存储,所以使用链表结构
    而进程需要查找用户,所以用Map结构,用户ID作为key
    public List<Tweet> allTweets;  // 所有推文
    public Map<Integer, TwitterUser> allUsers;  // 所有用户

    /** Initialize your data structure here. */
    public Twitter() {
        allTweets = new LinkedList<>();
        allUsers = new HashMap<>();  // 所有用户
    }

1.2 业务逻辑的处理

  1. 用户注册
    由于本题目本身的缺陷,并没有注册用户的入口
    在尝试提交结果的过程中,我发现,所有的用户都存在,换而言之,只要用户不存在,那就添加到用户列表
    public void signUpIfNotExisted(int userId) {
        if(allUsers.containsKey(userId)) return;
        Set<Integer> fans = new HashSet<>();
        Set<Integer> follower = new HashSet<>();
        allUsers.put(userId, new TwitterUser(userId, fans, follower));
    }
  1. 用户发推文
    为了便于去除最近发布的10条推文,我们可以在存储推文时,就按时间顺序存储
    public void postTweet(int userId, int tweetId) {
        signUpIfNotExisted(userId);
        Tweet tweet = new Tweet(tweetId, userId);
        allTweets.add(0, tweet);
    }
  1. 关注他人
    按照我们设计的数据结构,需要更新 当前用户的“我关注的人”列表,以及 被关注人的“关注我的人”列表
    注意:不能关注自己
    public void follow(int followerId, int followeeId) {  // 用户followerId关注了用户followeeId.
        if(followerId==followeeId) return;
        signUpIfNotExisted(followerId, followeeId);
        TwitterUser user = allUsers.get(followerId);  // 当前用户
        TwitterUser userFollowed = allUsers.get(followeeId);  // 被关注的人

        // 更新 followerId"我关注的人"列表
        HashSet<Integer> userFollow = (HashSet<Integer>) user.getFollower();
        userFollow.add(followeeId);
        user.setFollower(userFollow);

        // 更新 followeeId"关注我的人"列表
        HashSet<Integer> fans = (HashSet<Integer>) userFollowed.getFans();
        fans.add(followerId);
        userFollowed.setFans(fans);
    }
  1. 取消关注
    按照我们设计的数据结构,需要更新 当前用户的“我关注的人”列表,以及 被关注人的“关注我的人”列表
    注意:不能取消关注自己
    public void unfollow(int followerId, int followeeId) {
        if(followerId==followeeId) return;
        signUpIfNotExisted(followerId, followeeId);
        TwitterUser user = allUsers.get(followerId);  // 当前用户
        TwitterUser userFollowed = allUsers.get(followeeId);  // 被关注的人

        // 更新 followerId"我关注的人"列表
        HashSet<Integer> userFollow = (HashSet<Integer>) user.getFollower();
        userFollow.remove(followeeId);
        user.setFollower(userFollow);

        // 更新 followeeId"关注我的人"列表
        HashSet<Integer> fans = (HashSet<Integer>) userFollowed.getFans();
        fans.remove(followerId);
        userFollowed.setFans(fans);
    }
  1. 获取信息流
    按照题目要求,信息流中,最多只能有10条推文,且按时间倒序,且来自自己或关注人所发推文
    由于我们的推文已经按照时间倒序存储,可以很便捷地从前往后遍历,取出符合要求的10条推文即可。
    具体的要求就是:推文的发布者ID是当前用户ID,或者是当前用户关注任意人的ID
    public List<Integer> getNewsFeed(int userId) {
        signUpIfNotExisted(userId);
        TwitterUser user = allUsers.get(userId);
        HashSet<Integer> hisFollowerId = (HashSet<Integer>) user.getFollower();

        List<Integer> allTweetIdInFeed = new ArrayList<>(10);  // 信息流中的推文ID
        for(Tweet tweet: allTweets){
            Integer tweetUserId = tweet.getUserId();
            if(hisFollowerId.contains(tweetUserId) || tweetUserId==userId){
                allTweetIdInFeed.add(tweet.getId());
            }
            if(allTweetIdInFeed.size()>=10) return allTweetIdInFeed;
        }
        return allTweetIdInFeed;
    }

1.3 其他代码

04.15更新:先前的版本,以列表结构存储用户信息,所以需要写函数查询、删除用户,现在已经不需要

    // 根据ID查找用户
    public TwitterUser findByUserId(List<TwitterUser> allUsers, Integer userId){
        for(TwitterUser user: allUsers){
            if(user.getId().equals(userId)) return user;
        }
        return null;
    }

    public boolean removeUserById(List<Integer> userIds, Integer userId){
        for(Integer id: userIds){
            if(id.equals(userId)) return userIds.remove(id);
            // if(id==userId) return userIds.remove((Integer)id);
            // if(id==userId) return userIds.remove(new Integer(id));
        }
        return false;
    }

2. 测试验证

由于本题的设计缺陷,并未提供输入输出描述。所以,最开始,我的测试代码是这样的:

    @Test
    public void testCase1() {
        Twitter twitter = new Twitter();

        // 用户1发送了一条新推文 (用户id = 1, 推文id = 5).
        twitter.postTweet(1, 5);
        Assert.assertEquals(1, twitter.allTweets.size());
        Assert.assertEquals(1, twitter.allTweets.get(0).getUserId());
        Assert.assertEquals(5, twitter.allTweets.get(0).getId());

        // 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
        List<Integer> newsFeed1 = twitter.getNewsFeed(1);
        List<Integer> newsFeed1ans = Arrays.asList(new Integer[] {5});
        Assert.assertEquals(newsFeed1ans, newsFeed1);

        // 用户1关注了用户2.
        twitter.follow(1, 2);
        TwitterUser user = twitter.findByUserId(twitter.allUsers, 1);
        TwitterUser userFollowed = twitter.findByUserId(twitter.allUsers, 2);
        Assert.assertNotNull(user);
        Assert.assertTrue(user.getFollower().contains(2));
        Assert.assertNotNull(userFollowed);
        Assert.assertTrue(userFollowed.getFans().contains(1));

        // 用户2发送了一个新推文 (推文id = 6).
        twitter.postTweet(2, 6);
        Assert.assertEquals(2, twitter.allTweets.size());
        Assert.assertEquals(2, twitter.allTweets.get(0).getUserId());
        Assert.assertEquals(6, twitter.allTweets.get(0).getId());

        // 用户1的获取推文应当返回一个列表,其中包含两个推文,id分别为 -> [6, 5].
        // 推文id6应当在推文id5之前,因为它是在5之后发送的.
        List<Integer> newsFeed2 = twitter.getNewsFeed(1);
        List<Integer> newsFeed2ans = Arrays.asList(new Integer[] {6, 5});
        Assert.assertEquals(newsFeed2ans, newsFeed2);

        // 用户1取消关注了用户2.
        twitter.unfollow(1, 2);
        user = twitter.findByUserId(twitter.allUsers, 1);
        userFollowed = twitter.findByUserId(twitter.allUsers, 2);
        Assert.assertFalse(user.getFollower().contains(2));
        Assert.assertFalse(userFollowed.getFans().contains(1));

        // 用户1的获取推文应当返回一个列表,其中包含一个id为5的推文.
        // 因为用户1已经不再关注用户2.
        List<Integer> newsFeed3 = twitter.getNewsFeed(1);
        List<Integer> newsFeed3ans = Arrays.asList(new Integer[] {5});
        Assert.assertEquals(newsFeed3ans, newsFeed3);
    }

2.1 硬编码测试用例

2.1.1 运行推特

提交一次报错之后,在保存详情中,我看到了输入输出结构:

输入
["Twitter","postTweet","getNewsFeed","follow","postTweet","getNewsFeed","unfollow","getNewsFeed"]
[[],[1,5],[1],[1,2],[2,6],[1],[1,2],[1]]
输出
[null,null,[5],null,null,[6,5],null,[5]]

于是才想到,可以用 switch-case 的方式,来根据指令运行推特,代码大意如下:

    switch (op) {
        case "Twitter":
            twitter = new Twitter();  // 调用对应操作
            outputs.add(null);  // 记录该操作的输出
            break;
        case "postTweet":
            twitter.postTweet(params[0], params[1]);
            outputs.add(null);
            break;
        case "getNewsFeed":
            List<Integer> newsFeed = twitter.getNewsFeed(params[0]);
            outputs.add(newsFeed);
            break;
        case "follow":
            twitter.follow(params[0], params[1]);
            outputs.add(null);
            break;
        case "unfollow":
            twitter.unfollow(params[0], params[1]);
            outputs.add(null);
            break;
    }

2.1.2 使用用例测试推特

那么对应的单元测试可以写为:

    @Test
    public void run1() {
        DesignTwitter ins = new DesignTwitter();        
        // 输入:操作指令
        String[] operations1 = new String[] {"Twitter", "postTweet", "getNewsFeed", "follow", "postTweet", "getNewsFeed", "unfollow", "getNewsFeed"};
        // 输入:各个函数的入参
        Integer[][] values1 = new Integer[][] {{}, {1, 5}, {1}, {1, 2}, {2, 6}, {1}, {1, 2}, {1}};
        List<List<Integer>> valuesList1 = convert2DArrayTo2DList(values1); // 二位数组转成 元素为列表的列表
        // 输出用例
        Integer[][] outputsAns1 = new Integer[][] {null, null, {5}, null, null, {6, 5}, null, {5}};
        List<List<Integer>> outputsAnsList1 = convert2DArrayTo2DList(outputsAns1);

        List<List<Integer>> outputsList1 = ins.run(operations1, values1);  // 为了便于断言时比较,返回值设计为List
        Assert.assertEquals(outputsAnsList1, outputsList1);
    }

2.2 从文件中加载测试用例

但是,在基本确定代码之后,我仅通过一部分用例,而在一个非常大的用例上,出现空指针异常。

由于该用例非常大,不便甚至不能在代码中硬编码用例,只能通过输入获得。

于是设计了下面的代码从文件中加载用例

  1. 自定义类,用于表达输入测试用例
class InputTestCase {
    public String[] operations;
    public Integer[][] values;

    public TestCase(String[] operations, Integer[][] values) {
        this.operations = operations;
        this.values = values;
    }
}
  1. 读取文件
    测试用例的输入分为两行,第一行是操作名,第二行是对应操作的入参值
public class TestCaseLoader {

    // 加载输入用例
    public InputTestCase loadInput(String filepath) throws IOException {
        File testfile = new File(filepath);
        FileReader fileReader = new FileReader(testfile.getCanonicalPath());
        BufferedReader reader = new BufferedReader(fileReader);
        String line1 = reader.readLine();
        String[] operations = parseStringArray(line1);
        String line2 = reader.readLine();
        Integer[][] values = parse2DIntegerArray(line2);
        return new InputTestCase(operations, values);
    }

    // 加载输出用例
    public Integer[][] loadOutput(String filepath) throws IOException {
        File testfile = new File(filepath);
        FileReader fileReader = new FileReader(testfile.getCanonicalPath());
        BufferedReader reader = new BufferedReader(fileReader);
        String line = reader.readLine();
        Integer[][] value = parse2DIntegerArray(line);
        return value;
    }

    // 输入形式为:字符串数组
    String[] parseStringArray(String line){
        JSONArray jsonArray1 = JSONArray.parseArray(line);
        String[] strArray = jsonArray1.toArray(new String[]{});
        return strArray;
    }

    // 输入形式为:二维整数数组
    Integer[][] parse2DIntegerArray(String line){
        JSONArray jsonArray2 = JSONArray.parseArray(line);
        JSONArray[] jsonArray2Tmp = jsonArray2.toArray(new JSONArray[]{});
        Integer[][] values = new Integer[jsonArray2Tmp.length][];
        for(int i=0; i< jsonArray2Tmp.length; i++){
            JSONArray intArrTmp = jsonArray2Tmp[i];
            if(intArrTmp==null) {
                values[i] = null;
            }else {
                values[i] = intArrTmp.toArray(new Integer[] {});
            }
        }
        return values;
    }
}
  1. 从文件中加载用例的单元测试写法
    @Test
    public void run5() throws IOException {
        TestCaseLoader loader = new TestCaseLoader();
        String inputFile = "src/main/resource/design_twitter/62405019_input.txt";  // 输入用例的路径
        String outputFile = "src/main/resource/design_twitter/62405019_output.txt";
        InputTestCase case1 = loader.loadInput(inputFile);
        Integer[][] outputsAns1 = loader.loadOutput(outputFile);

        DesignTwitter ins = new DesignTwitter();
        String[] operations1 = case1.operations;
        Integer[][] values1 = case1.values;
        List<List<Integer>> outputsAnsList1 = convert2DArrayTo2DList(outputsAns1);

        List<List<Integer>> outputsList1 = ins.run(operations1, values1);
        Assert.assertEquals(outputsAnsList1, outputsList1);
    }

2.3 数据结构转换的代码

    // 二维数组转二维列表
    List<List<Integer>> convert2DArrayTo2DList(Integer[][] array2d) {
        List<List<Integer>> list2d = new ArrayList<>();
        for (int i = 0; i < array2d.length; i++) {
            List<Integer> columnList = new ArrayList<>();
            if (array2d[i] == null) {
                list2d.add(null);
                continue;
            }
            for (int j = 0; j < array2d[i].length; j++) {
                columnList.add(array2d[i][j]);
            }
            list2d.add(columnList);
        }
        return list2d;
    }

3. 完整题解代码

class Twitter {

    public List<Tweet> allTweets;  // 所有推文
    public Map<Integer, TwitterUser> allUsers;  // 所有用户

    /** Initialize your data structure here. */
    public Twitter() {
        allTweets = new LinkedList<>();
        allUsers = new HashMap<>();  // 所有用户
    }

    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) {
        signUpIfNotExisted(userId);
        Tweet tweet = new Tweet(tweetId, userId);
        allTweets.add(0, tweet);
    }

    /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    public List<Integer> getNewsFeed(int userId) {
        signUpIfNotExisted(userId);
        TwitterUser user = allUsers.get(userId);
        HashSet<Integer> hisFollowerId = (HashSet<Integer>) user.getFollower();

        List<Integer> allTweetIdInFeed = new ArrayList<>(10);  // 信息流中的推文ID
        for(Tweet tweet: allTweets){
            Integer tweetUserId = tweet.getUserId();
            if(hisFollowerId.contains(tweetUserId) || tweetUserId==userId){
                allTweetIdInFeed.add(tweet.getId());
            }
            if(allTweetIdInFeed.size()>=10) return allTweetIdInFeed;
        }
        return allTweetIdInFeed;
    }

    /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    public void follow(int followerId, int followeeId) {  // 用户followerId关注了用户followeeId.
        if(followerId==followeeId) return;
        signUpIfNotExisted(followerId, followeeId);
        TwitterUser user = allUsers.get(followerId);  // 当前用户
        TwitterUser userFollowed = allUsers.get(followeeId);  // 被关注的人

        // 更新 followerId"我关注的人"列表
        HashSet<Integer> userFollow = (HashSet<Integer>) user.getFollower();
        userFollow.add(followeeId);
        user.setFollower(userFollow);

        // 更新 followeeId"关注我的人"列表
        HashSet<Integer> fans = (HashSet<Integer>) userFollowed.getFans();
        fans.add(followerId);
        userFollowed.setFans(fans);
    }

    /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    public void unfollow(int followerId, int followeeId) {
        if(followerId==followeeId) return;
        signUpIfNotExisted(followerId, followeeId);
        TwitterUser user = allUsers.get(followerId);  // 当前用户
        TwitterUser userFollowed = allUsers.get(followeeId);  // 被关注的人

        // 更新 followerId"我关注的人"列表
        HashSet<Integer> userFollow = (HashSet<Integer>) user.getFollower();
        userFollow.remove(followeeId);
        user.setFollower(userFollow);

        // 更新 followeeId"关注我的人"列表
        HashSet<Integer> fans = (HashSet<Integer>) userFollowed.getFans();
        fans.remove(followerId);
        userFollowed.setFans(fans);
    }

    public void signUpIfNotExisted(int userId) {
        if(allUsers.containsKey(userId)) return;
        Set<Integer> fans = new HashSet<>();
        Set<Integer> follower = new HashSet<>();
        allUsers.put(userId, new TwitterUser(userId, fans, follower));
    }

    public void signUpIfNotExisted(int... userIdList) {
        for(int userId: userIdList) {
            if (allUsers.containsKey(userId))
                continue;
            Set<Integer> fans = new HashSet<>();
            Set<Integer> follower = new HashSet<>();
            allUsers.put(userId, new TwitterUser(userId, fans, follower));
        }
    }
}

/* 自定义用户类 */
class TwitterUser {
    private Integer id;
    private Set<Integer> fans;  // 关注我的人
    private Set<Integer> follower;  // 我关注的人

    public TwitterUser(Integer id, Set<Integer> fans, Set<Integer> follower) {
        this.id = id;
        this.fans = fans;
        this.follower = follower;
    }

    public Integer getId() {
        return id;
    }

    public Set<Integer>  getFans() {
        return fans;
    }

    public Set<Integer> getFollower() {
        return follower;
    }

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

    public void setFans(Set<Integer> fans) {
        this.fans = fans;
    }

    public void setFollower(Set<Integer> follower) {
        this.follower = follower;
    }
}

/* 自定义 推文结构 */
class Tweet {
    private Integer id;
    private Integer userId;

    public Tweet(Integer id, Integer userId) {
        this.id = id;
        this.userId = userId;
    }

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

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getId() {
        return id;
    }

    public Integer getUserId() {
        return userId;
    }
}

4. 完整的启动推特和测试推特代码

4.1 启动推特

public class TwitterStarter {

    Twitter twitter;

    List<List<Integer>> run(String[] operations, Integer[][] values){
        List<List<Integer>> outputs = new ArrayList<>();
        for(int i=0; i< operations.length; i++){
            String op = operations[i];
            Integer[] params =values[i];
            switch (op) {
                case "Twitter":
                    twitter = new Twitter();
                    outputs.add(null);
                    break;
                case "postTweet":
                    twitter.postTweet(params[0], params[1]);
                    outputs.add(null);
                    break;
                case "getNewsFeed":
                    List<Integer> newsFeed = twitter.getNewsFeed(params[0]);
                    outputs.add(newsFeed);
                    break;
                case "follow":
                    twitter.follow(params[0], params[1]);
                    outputs.add(null);
                    break;
                case "unfollow":
                    twitter.unfollow(params[0], params[1]);
                    outputs.add(null);
                    break;
            }
        }
        return outputs;
    }
}

4.2 加载用例

public class TestCaseLoader {
    public InputTestCase loadInput(String filepath) throws IOException {
        File testfile = new File(filepath);
        FileReader fileReader = new FileReader(testfile.getCanonicalPath());
        BufferedReader reader = new BufferedReader(fileReader);
        String line1 = reader.readLine();
        String[] operations = parseStringArray(line1);
        String line2 = reader.readLine();
        Integer[][] values = parse2DIntegerArray(line2);
        return new InputTestCase(operations, values);
    }

    public Integer[][] loadOutput(String filepath) throws IOException {
        File testfile = new File(filepath);
        FileReader fileReader = new FileReader(testfile.getCanonicalPath());
        BufferedReader reader = new BufferedReader(fileReader);
        String line = reader.readLine();
        Integer[][] value = parse2DIntegerArray(line);
        return value;
    }

    String[] parseStringArray(String line){
        JSONArray jsonArray1 = JSONArray.parseArray(line);
        String[] strArray = jsonArray1.toArray(new String[]{});
        return strArray;
    }

    Integer[][] parse2DIntegerArray(String line){
        JSONArray jsonArray2 = JSONArray.parseArray(line);
        JSONArray[] jsonArray2Tmp = jsonArray2.toArray(new JSONArray[]{});
        Integer[][] values = new Integer[jsonArray2Tmp.length][];
        for(int i=0; i< jsonArray2Tmp.length; i++){
            JSONArray intArrTmp = jsonArray2Tmp[i];
            if(intArrTmp==null) {
                values[i] = null;
            }else {
                values[i] = intArrTmp.toArray(new Integer[] {});
            }
        }
        return values;
    }
}

class InputTestCase {
    public String[] operations;
    public Integer[][] values;

    public InputTestCase(String[] operations, Integer[][] values) {
        this.operations = operations;
        this.values = values;
    }
}

4.3 测试推特

import org.junit.*;

public class TwitterStarterTest {
    @Test
    public void run5() throws IOException {
        TestCaseLoader loader = new TestCaseLoader();
        String inputFile = "src/main/resource/design_twitter/62405019_input.txt";
        String outputFile = "src/main/resource/design_twitter/62405019_output.txt";
        InputTestCase case1 = loader.loadInput(inputFile);
        Integer[][] outputsAns1 = loader.loadOutput(outputFile);

        DesignTwitter ins = new DesignTwitter();
        String[] operations1 = case1.operations;
        Integer[][] values1 = case1.values;
        List<List<Integer>> outputsAnsList1 = convert2DArrayTo2DList(outputsAns1);

        List<List<Integer>> outputsList1 = ins.run(operations1, values1);
        Assert.assertEquals(outputsAnsList1, outputsList1);
    }
    
    // 二维数组转二维列表
    List<List<Integer>> convert2DArrayTo2DList(Integer[][] array2d) {
        List<List<Integer>> list2d = new ArrayList<>();
        for (int i = 0; i < array2d.length; i++) {
            List<Integer> columnList = new ArrayList<>();
            if (array2d[i] == null) {
                list2d.add(null);
                continue;
            }
            for (int j = 0; j < array2d[i].length; j++) {
                // columnList.add(j, array2d[i][j]);
                columnList.add(array2d[i][j]);
            }
            // list2d.add(i, columnList);
            list2d.add(columnList);
        }
        return list2d;
    }
}

附录

  1. 部分测试用例

输入:https://leetcode-cn.com/submissions/detail/62405019/testcase/
输出:用上文中给的单元测试运行一下前面的输入用例,即可得到

  1. 为什么会出现空指针异常

    查找用户的函数,需要比较用户ID,开始是用 ==,在上面这个用例时,有一对取消关注(134,128)会报空指针异常,位置如下:

     public void unfollow(int followerId, int followeeId) {
     if(followerId==followeeId) return;
     signUpIfNotExisted(followerId, followeeId);
     TwitterUser user = findByUserId(allUsers, followerId);  // 当前用户
     TwitterUser userFollowed = findByUserId(allUsers, followeeId);  // 被关注的人
    
     List<Integer> userFollow = user.getFollower();  // 空指针异常发生位置
    

    由于signUpIfNotExisted的存在,显然不可能有"用户不存在"的问题,原因显然是findByUserId返回值为null。这才发现,同一个数值可能有多个Integer对象

  2. 从Integer列表中,根据元素值移除该元素
    List对象有两个重载方法:
    删除指定位置的元素:

    list.remove(int index);
    

    删除指定值的元素:

    list.remove(Object obj);
    

    但是,当列表为Integer列表时,JVM可能会疑惑,需要注意。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容