构建对象集合需要很长时间

时间:2016-02-28 19:55:41

标签: java arraylist

我要做的是从我UserObjects

中读取的ArrayList<String>中构建BufferedReader的集合

UserObject只包含以下字段:

int UserId ArrayList<Integer> AssociatesId

我当前的代码使用BufferedReader来读取file.edgelist并构建ArrayList<String>,其中包含以下格式的条目:“1 1200”

我正在通过其空格将该字符串拆分为String[]并使用UserObject构建新的UserId = 1并初始化一个新的ArrayList<Integer>,其中包含第二个元素中的任何整数具有相同的UserId

我的问题是file.edgelist有大约20,000,000个条目,而BufferedReader读取文件的时间不到10秒,构建UserObjects的集合需要花费很长时间。事实上,我甚至没有到达文件的末尾,因为它需要很长时间。我可以确认我已成功构建这些条目,因为我在调试中运行代码并删除了偶尔的断点,以发现UserId正在增加,UserObject的{​​{1}}集合包含数据。

是否有更快捷和/或更好的方法来构建此集合?

这是我目前的代码:

AssociatesId

3 个答案:

答案 0 :(得分:2)

每次调用getUser时,都会遍历整个列表以检查给定用户是否存在。这是非常低效的,因为列表的大小正在增长(在最坏的情况下线性复杂性)。您可能希望将其替换为HashMap(查找具有恒定的复杂性)。

private Map<Integer, UserObject> tempUsers = new HashMap();

//helper method that uses Stream to find and return existing UserObject
private UserObject getUser(int id){
    return users.get(id);
}

此外,创建具有20,000,000,000个条目的中间ArrayList<String> userStr是完全没必要的,浪费了大量内存。在读取阅读器的行时,您应该创建UserObject个实例。

答案 1 :(得分:1)

哇,你只是在那里浪费记忆和表现。

首先,不要将整个文件作为List<String>加载到内存中。这完全是对内存的浪费。将文件直接加载到UserObject个对象中。

接下来,不要将它们存储为List<UserObject>,并按id执行顺序搜索对象。那只是.... sllloooooooooowwwww ....

您应将其存储在Map<Integer, UserObject>中,以便id快速访问。

实际上,您甚至不需要UserObject。根据您的说法,您只需要一个Map<Integer, List<Integer>>,也称为MultiMap。这很简单,你可以找到自己的第三方库。

另外,不要使用split()你知道每一行只包含1个空格。使用indexOf()substring()

答案 2 :(得分:1)

您的代码符合“管道”的定义,因此可以从更明智地使用Streams API中获益。例如,您不需要将整个文件读入内存,只需使用Files.lines获取文件中每一行的Stream<String>。此外,您可以像以下一样进行解析:

//Where the problem actually lies
public ArrayList<UserObject> BuildUsers(Stream<String> userStrings){
    java.util.Map<Integer,UserObject> users = userStrings // Stream<String>
        .map(str -> s.split("\\s+")) // Stream<String[]>
        .map(ids -> {
            UserObject newUser = new UserObject(Integer.parseInt(ids[0]));
            newUser.associate(Integer.parseInt(ids[1]));
            return newUser;
        }) // Stream<UserObject>, all new (maybe with duplicated ids)
        .collect(Collectors.groupingBy(
               uObj -> uObj.getId(), // whatever returns the "ids[0]" value
               java.util.HashMap::new,
               Collectors.reducing((uo1, uo2) -> {
                   // This lambda "merges" uo2 into uo1
                   uo2.getAssociates().forEach(uo1::associate);
                   return uo1;
               })));
    return new ArrayList<>(users.values());
}

我在UserObject中编写了“getId”和“getAssociates”函数,以返回最初来自ids数组元素的值。此函数首先将每一行拆分为一个String数组,然后将每个2元素数组解析为 new UserObject实例。最终收藏家执行两项职能:

  • 按Id属性进行分组,因此获取Map<Integer,List<UserObject>>所有具有相同主ID的UserObject。
  • 将具有相同主ID的几个UserObject实例(压缩)压缩(压缩)到单个实例中(每Collectors.reducing个),以便最终实际获得Map<Integer,UserObject> 。传递给reducing的函数接受两个UserObject实例,并返回一个包含其两个“父”的关联ID的实例。

最后,因为显然你想要一个带有值的ArrayList,所以代码只是从地图中获取它们并将它们转储到所需的容器类型中。