我有一个自定义对象的ArrayList(DTO),DTO的结构:
private String id;
private String text;
private String query;
private String locatorId;
private Collection<String> categories;
private Collection<String> triggers;
我有两项任务:
这项任务最有效的方法是什么?我在算法中使用 Lambda 表达式也很有意思。
答案 0 :(得分:4)
使用流API按指定键合并对象非常容易。首先,在merge
类中定义Entity
方法,如下所示:
public Entity merge(Entity other) {
this.categories.addAll(other.categories);
this.triggers.addAll(other.triggers);
return this;
}
然后您可以构建自定义分组收集器:
import static java.util.stream.Collectors.*;
public static Collection<Entity> mergeAll(Collection<Entity> input) {
return input.stream()
.collect(groupingBy(Entity::getId,
collectingAndThen(reducing(Entity::merge), Optional::get)))
.values();
}
在这里,我们按Entity
方法的结果对getId
元素进行分组,下游收集器在遇到相同的Entity.merge()
时调用id
(我们需要在{{ 1}}另外)。此解决方案中Optional
无需特殊hashCode()
或equals()
实施。
请注意,此解决方案会修改现有的未合并Entity
个对象。如果不合适,请在Entity
方法中创建一个新的Entity
并将其返回(如@ Marco13答案中所示)。
答案 1 :(得分:2)
创建Map<Integer, DTO>
并将您的ID作为密钥和对象放置为DTO。在放入map之前,只需检查它是否已包含该键,如果它确实包含该键,则取出该键的DTO对象,并将类别和触发器与旧对象合并。
答案 2 :(得分:2)
正如answer by Naman Gala中所建议的,一种可能的解决方案是使用ID中的some case class
case class Foo(id: Int, year: Int)
实体,并在实体具有相同ID时手动合并。
这是在Map
方法中实现的,其中一些虚拟/示例输入
mergeById
输出
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class MergeById
{
public static void main(String[] args)
{
List<Entity> entities = new ArrayList<Entity>();
entities.add(new Entity("0", "A", "X", "-1",
Arrays.asList("C0", "C1"), Arrays.asList("T0", "T1")));
entities.add(new Entity("0", "A", "X", "-1",
Arrays.asList("C2", "C3"), Arrays.asList("T2")));
entities.add(new Entity("1", "B", "Y", "-2",
Arrays.asList("C0"), Arrays.asList("T0", "T1")));
entities.add(new Entity("1", "B", "Y", "-2",
Arrays.asList("C0"), Arrays.asList("T0", "T1")));
entities.add(new Entity("2", "C", "Z", "-3",
Arrays.asList("C0", "C1"), Arrays.asList("T1")));
System.out.println("Before merge:");
for (Entity entity : entities)
{
System.out.println(entity);
}
List<Entity> merged = mergeById(entities);
System.out.println("After merge:");
for (Entity entity : merged)
{
System.out.println(entity);
}
}
private static List<Entity> mergeById(Iterable<? extends Entity> entities)
{
Map<String, Entity> merged = new HashMap<String, Entity>();
for (Entity entity : entities)
{
String id = entity.getId();
Entity present = merged.get(id);
if (present == null)
{
merged.put(id, entity);
}
else
{
merged.put(id, Entity.merge(present, entity));
}
}
return new ArrayList<Entity>(merged.values());
}
}
class Entity
{
private String id;
private String text;
private String query;
private String locatorId;
private Collection<String> categories;
private Collection<String> triggers;
Entity()
{
categories = new LinkedHashSet<String>();
triggers = new LinkedHashSet<String>();
}
Entity(String id, String text, String query, String locatorId,
Collection<String> categories, Collection<String> triggers)
{
this.id = id;
this.text = text;
this.query = query;
this.locatorId = locatorId;
this.categories = categories;
this.triggers = triggers;
}
String getId()
{
return id;
}
static Entity merge(Entity e0, Entity e1)
{
if (!Objects.equals(e0.id, e1.id))
{
throw new IllegalArgumentException("Different id");
}
if (!Objects.equals(e0.text, e1.text))
{
throw new IllegalArgumentException("Different text");
}
if (!Objects.equals(e0.query, e1.query))
{
throw new IllegalArgumentException("Different query");
}
if (!Objects.equals(e0.locatorId, e1.locatorId))
{
throw new IllegalArgumentException("Different id");
}
Entity e = new Entity(e0.id, e0.text, e0.query, e0.locatorId,
new LinkedHashSet<String>(), new LinkedHashSet<String>());
e.categories.addAll(e0.categories);
e.categories.addAll(e1.categories);
e.triggers.addAll(e0.triggers);
e.triggers.addAll(e1.triggers);
return e;
}
@Override
public String toString()
{
return "Entity [id=" + id + ", text=" + text + ", query=" + query +
", locatorId=" + locatorId + ", categories=" + categories +
", triggers=" + triggers + "]";
}
}
关于使用lambdas执行此操作的请求:可能编写一些棘手的Before merge:
Entity [id=0, text=A, query=X, locatorId=-1, categories=[C0, C1], triggers=[T0, T1]]
Entity [id=0, text=A, query=X, locatorId=-1, categories=[C2, C3], triggers=[T2]]
Entity [id=1, text=B, query=Y, locatorId=-2, categories=[C0], triggers=[T0, T1]]
Entity [id=1, text=B, query=Y, locatorId=-2, categories=[C0], triggers=[T0, T1]]
Entity [id=2, text=C, query=Z, locatorId=-3, categories=[C0, C1], triggers=[T1]]
After merge:
Entity [id=0, text=A, query=X, locatorId=-1, categories=[C0, C1, C2, C3], triggers=[T0, T1, T2]]
Entity [id=1, text=B, query=Y, locatorId=-2, categories=[C0], triggers=[T0, T1]]
Entity [id=2, text=C, query=Z, locatorId=-3, categories=[C0, C1], triggers=[T1]]
应用程序。但由于这不是问题的主要目标,我将把这部分答案留给别人(但不会遗漏这个小提示:只是因为你不能意味着你必须这样做。有时,一个循环很好)。
另请注意,这很容易推广,可能会从数据库中提供一些词汇。但我认为应该回答问题的要点。
答案 3 :(得分:1)
根据DTO中的equals
字段实施hashCode
和id
,并将DTO存储在Set
中。这应该解决你的两个问题;鉴于现在定义了DTO的相等方式,id
中不存在具有相同Set
的重复项。
编辑:
由于您的要求是根据新DTO的值合并现有DTO的类别和触发器,因此用于存储DTO
的更合适的数据结构将是Map<DTO, DTO>
(因为它很麻烦一旦你把元素放进去,就从Set
中检索元素。另外,我认为DTO
中的类别和触发器应定义为Set
s,禁止重复;这将使合并操作更加简单:
private Set<String> categories;
private Set<String> triggers;
假设DTO
为上述字段提供了访问者(getCategories
/ getTriggers
)(并且字段永远不会是null
),现在可以在以下方式:
public static void mergeOrPut(Map<DTO,DTO> dtos, DTO dto) {
if (dtos.containsKey(dto)) {
DTO existing = dtos.get(dto);
existing.getCategories().addAll(dto.getCategories());
existing.getTriggers().addAll(dto.getTriggers());
} else {
dtos.put(dto, dto);
}
}
也可以轻松修改上述代码以使用Map<Integer, DTO>
,在这种情况下,您无需覆盖equals
类中的hashCode
和DTO
答案 4 :(得分:1)
如果您坚持使用lambda表达式,则可以执行以下操作:
Set<X> x = new TreeSet<>((o1, o2) ->
((X)o1).getId().equals(((X)o2).getId()) ? 0 : 1);
List<X> list = new ArrayList<>(set.addAll(x));
这将根据其ID创建具有唯一对象的集合。接下来,对于list
中的每个对象,从原始列表中找到相应的对象并合并内部集合。