代码优化将Java中的整数列表转换为对象列表



我有大约50000到500000个员工ID,我希望这些员工ID转换为详细信息对象。

我做了这样的事情来实现这一点:

private Set<Detail> setDetail(List<Integer> employees, Group group) {
Set<Detail> details = employees.stream().parallel().map(id -> new Detail(id, group)).collect(Collectors.toSet());
return details ;
}

但这非常缓慢,而且随着员工ID数量的增加,速度越来越慢。如何优化此代码?我可以使用哪些优化技术/算法来更好地优化这一点。

您应该尽量避免创建那么多对象。无论你选择哪种算法,如果你的数据库在某个时候继续增长,你都无法将其全部放入内存。此外,瓶颈可能是从数据库获取数据(而不是创建对象(。

因此,请尝试重新构建您的应用程序,以便在执行相关操作时预先计算数据并将其存储在DB中。

如果经过仔细考虑,您决定确实需要处理那么多对象,那么更好的选择是继续使用基元:

class EmployeesInGroup {
private final int[] ids;
private final Group group;
...
Detail get(int idx) {
return new Details(ids[idx], group);
}
int size() {
return ids.length;
}
}

然后你可以迭代这个列表,一次处理一个对象,不需要在内存中保存很多对象:

EmployeesInGroup list = new EmployeesInGroup(ids, group);
for(int i = 0; i < list.size(); i++) {
Detail d = list.get(i);
...
}

您可以使它实现Iterable并用于每个循环。

基准

我上面列出的方法比创建Detail对象的数组至少快20倍。使用流和列表会进一步降低速度。我没有检查Integer,但我预测它会使一切速度再慢2倍左右。

Benchmark                                        Mode  Cnt     Score    Error  Units
EmployeeConversionBenchmark.objectArray         thrpt   20   368.702 ±  3.483  ops/s
EmployeeConversionBenchmark.primitiveArray      thrpt   20  7595.080 ± 68.841  ops/s
EmployeeConversionBenchmark.streamsWithObjects  thrpt   20   197.923 ±  1.616  ops/s

这是我使用的代码:

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import static java.util.stream.Collectors.toList;
public class EmployeeConversionBenchmark {
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(new String[]{EmployeeConversionBenchmark.class.getSimpleName()});
}
@Benchmark @Fork(value = 1, warmups = 0)
public int primitiveArray(Data data) {
EmployeesInGroup e = new EmployeesInGroup(data.ids, data.group);
int sum = 0;
for (int i = 0; i < e.size(); i++)
sum += e.get(i).getId();
return sum;
}
@Benchmark @Fork(value = 1, warmups = 0)
public int objectArray(Data data) {
EmployeesInGroup.Detail[] e = new EmployeesInGroup.Detail[data.ids.length];
for (int i = 0; i < data.ids.length; i++)
e[i] = new EmployeesInGroup.Detail(data.ids[i], data.group);
int sum = 0;
for (EmployeesInGroup.Detail detail : e)
sum += detail.getId();
return sum;
}
@Benchmark @Fork(value = 1, warmups = 0)
public int streamsWithObjects(Data data) {
List<EmployeesInGroup.Detail> e = Arrays.stream(data.ids).mapToObj(id -> new EmployeesInGroup.Detail(id, data.group)).collect(toList());
int sum = 0;
for (EmployeesInGroup.Detail detail : e)
sum += detail.getId();
return sum;
}
@State(Scope.Benchmark)
public static class Data {
private final int[] ids = new int[500_000];
private final EmployeesInGroup.Group group = new EmployeesInGroup.Group();
public Data() {
for (int i = 0; i < ids.length; i++)
ids[i] = new Random().nextInt();
}
}
public static class EmployeesInGroup {
private final int[] ids;
private final Group group;
public EmployeesInGroup(int[] ids, Group group) {
this.ids = ids;
this.group = group;
}
public Detail get(int idx) {
return new Detail(ids[idx], group);
}
public int size() {
return ids.length;
}
public static class Group {
}
public static class Detail {
private final int id;
private final Group group;
public Detail(int id, Group group) {
this.id = id;
this.group = group;
}
public int getId() {
return id;
}
}
}
}

通常,关于性能和优化的对话应该包括数字,例如:您试图实现的延迟、您实际观察到的延迟、有关硬件的一些细节等

你的问题(如果有的话(不在你发布的方法上。在我的笔记本电脑上,下面的代码在大约250毫秒内执行,如果删除parallel():,则执行速度会更快(大约200毫秒(

class Detail {
private Integer id;
private String group;
public Detail(Integer id, String group) {
this.id = id;
this.group = group;
}
}
public class Main {
public Set<Detail> setDetail(List<Integer> employees, String group) {
return employees.stream().parallel().map(id -> new Detail(id, group)).collect(toSet());
}
public static void main(String[] args) {
List<Integer> idsList = new Random().ints().boxed().limit(500_000).collect(toList());
long start = System.currentTimeMillis();
Set<Detail> details = new Main().setDetail(idsList, "group");
long duration = (System.currentTimeMillis() - start);
System.out.println("Done in " + duration + " ms. Size was " + details.size());
}
}

最新更新