我有一个简单的类:
public class Thing {
Long id;
String color;
String size;
}
我有两个List<Thing>
对象,看起来像这样:
List<Thing> colors = [{2, red}, {3, blue}];
List<Thing> sizes = [{2, small}, {3, large}];
使用Java streams/forEach;流";一个进入另一个到达:
List<Thing> things = [{2, red, small}, {3, blue, large}]
这个概念(将两个长度相等的流混合在一起,这样你就可以从每个流中获得一个元素,它们都传递给一个函数,然后将组合映射到其他函数(被称为zipping。
Java的流类不包括对该操作的任何支持。除非你能以某种方式保证两者的"id"概念以完全相同的顺序发生(即,如果你的sizes
是[{3, large}, {2, small}]
,我们不再谈论zip操作(,否则你在这里所拥有的也不是可以有意义地压缩在一起的东西。
在前一种情况下(这不是关于"组合red和small,因为它们都有id2
,而是关于将colors
中第一个元素的2
和red
与sizes
中第一个元件的small
组合起来,而不考虑id(,您可以从另一个库中找到zip支持,也可以破解它:
if (colors.size() != sizes.size()) throw new IllegalStateException();
IntStream.range(0, colors.size())
.mapToObj(i -> new Thing(colors.get(i).getId(), colors.get(i).getColor(), sizes.get(i).getSize()))
.collect(Collectors.toList());
它看起来很难看,如果列表不是随机访问的话,它的性能糟糕得离谱,在我所能理解的几乎所有方面,与只制作2个迭代器并并行迭代它们相比,这真是令人失望:
if (colors.size() != sizes.size()) throw new IllegalStateException();
var itColors = colors.iterator();
var itSizes = sizes.iterator();
var out = new ArrayList<Thing>();
while (itColors.hasNext()) {
Thing a = itColors.next();
out.add(new Thing(a.getId(), a.getColor(), itSizes.next().getSize());
}
如果你想这样做,你一开始就绝对不应该使用流。
不,是关于身份证的
在这种情况下,流似乎很像你用一把非常闪亮的花式锤子把果酱涂在三明治上:流只是。。。在这里毫无意义。完全你可以尝试一些古怪的东西,好吧,让这一切发生,并使用流,但这将是一个古怪的代码:它看起来很奇怪,不地道,包括大量的警告(假设,如果不是真的,意味着代码没有做好工作,或者在某些情况下具有极其糟糕的性能特征(,通常情况下会更糟。
解决方案是地图:
var out = new HashMap<Long, Thing>();
for (Thing c : colors) out.put(c.getId(), c);
for (Thing s : sizes) out.merge(c.getId(), s,
(a, b) -> new Thing(a.getId(), a.getColor(), b.getSize());
看看它有多干净,它还处理输入之间的任何不匹配(大小包含id 87,但颜色不包含(。
我会创建一个从id
到Thing
的Map<Long, Thing>
,并首先放置有颜色的东西,然后我会为地图上的东西设置大小,前提是它们存在:
Map<Long, Thing> map = new LinkedHashMap<>();
colors.forEach(t -> map.put(
t.getId(),
new Thing(t.getId(), t.getColor(), null))); // assumes constructor from attributes
sizes.forEach(t -> {
Thing thing = map.get(t.getId());
if (thing != null) thing.setSize(t.getSize());
});
这使原始Thing
保持不变。