我要做的是从我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
答案 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实例。最终收藏家执行两项职能:
Map<Integer,List<UserObject>>
所有具有相同主ID的UserObject。Map<Integer,UserObject>
。传递给reducing
的函数接受两个UserObject实例,并返回一个包含其两个“父”的关联ID的实例。最后,因为显然你想要一个带有值的ArrayList,所以代码只是从地图中获取它们并将它们转储到所需的容器类型中。