合并两个没有重复子类的ArrayLists

时间:2018-02-14 05:39:43

标签: java arraylist

假设:

public abstract class Cars {}

...

public class Ford extends Cars {}

...

public class Dodge extends Cars {}

...

public class Volkswagen extends Cars {}

...

如果我有两个ArrayList对象:

List<Cars> dealer1 = new ArrayList<>;
List<Cars> dealer2 = new ArrayList<>;

dealer1.addAll(asList(new Ford("ford1"), new Dodge("dodge1")));
dealer2.addAll(asList(new Dodge("dodge2"), new Volkswagen("vw1")));

然后我想从两个中创建一个合并列表,每个子类只有一个实例,这样:

dealerMerged = ["ford1", "dodge1", "vw1"]

OR

dealerMerged = ["ford1", "dodge2", "vw1"]

哪个实例进入合并列表并不重要。

这可能吗?我进行了搜索并看到了有关使用Set的内容,但这似乎只能确保唯一的引用,除非我误解了一些内容。

4 个答案:

答案 0 :(得分:1)

覆盖equals()将起作用,但不要

您可以随时将您的收藏品区分开来,将其转换为Set(评论中为@Arun状态)或对收藏集的流进行distinct操作。但请记住,这些方法使用equal()方法。因此,快速思考将覆盖equals()并返回其Class类型。可是等等 !如果这样做,您将最终让所有Dodge个对象彼此相等,尽管它们具有不同的属性,如名称dodge1dodge2。您可能不仅处理阅读世界中的单个业务。 equal()方法有很多其他意义。所以不要这样做。

如果您正在考虑Java 8方式,则状态过滤器是完美的

我们可以选择对连接流使用filter操作。 filter操作非常直接。它需要一个谓词并决定采用或忽略哪个元素。这是一个常用的功能,您可以在整个博客中找到解决此问题的功能。

public static <T> Predicate<T> distinctBy(Function<? super T, ?> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

这里distinctBy函数返回一个谓词(将在filter操作中使用)。它保持关于之前看到的内容的状态,并返回是否第一次看到给定元素。 (您可以阅读有关此here

的进一步说明

您可以使用此状态过滤器,如

Stream.of(dealer1, dealer2)
        .flatMap(Collection::stream)
        .filter(distinctBy(Cars::getClass))
        .collect(Collectors.toList())
        .forEach(cars -> System.out.println(cars));

那我们在这里做了什么?

  • 我们将2 ArrayListflatmap连接在一起,这将为我们提供合并元素的单个流(如果您不熟悉Stream API。请参阅此Stream Concatenation文章
  • 然后,我们利用返回谓词的filter()方法提供的distinctBy操作。
  • 并且您看到维护ConcurrentHashMap以通过布尔标志跟踪哪个元素满足谓词。
  • 谓词使用getClass()方法返回完整的类名,将元素区分为子类
  • 然后我们可以collect或迭代过滤后的列表。

答案 1 :(得分:1)

尝试使用Map而不是List。您可以尝试以下解决方案。这将允许您按类型放置Car实例。因此,每个类总是只有一个条目(顺便说一下,这将是地图中的最新条目)。

public class CarsCollection {
    Map<Class<? extends Cars>, ? super Cars> coll = new HashMap<>();

    public <T extends Cars> void add(Class<T> cls, T obj) {
        coll.put(cls, obj);
    }
}

public class Temp {

    public static void main(String[] args)  {

        CarsCollection nos1 = new CarsCollection();

        cars.add(Ford.class, new Ford("ford1"));
        cars.add(Dodge.class, new Dodge("dodge1"));

        cars.add(Dodge.class, new Dodge("dodge2"));
        cars.add(Volkswagen.class, new Volkswagen("vw1"));

        System.out.println(cars);
    }

}

答案 2 :(得分:0)

您可以将第一个列表的所有元素添加到结果列表中(假设第一个列表中没有重复),然后循环遍历第二个列表,并仅在没有实例的情况下将元素添加到结果列表中第一个列表中的同一个类。

看起来像这样:

dealerMerged = dealer1;
boolean isAlreadyRepresented;
for (car2 : dealer2) {
    isAlreadyRepresented = false;
    for (car1 : dealer1) {
        if (car1.getClass().equals(car2.getClass())) {
            isAlreadyRepresented = true;
        }
    }
    if (!isAlreadyRepresented) {
        dealerMerged.add(car2);
    }
}

答案 3 :(得分:0)

只需将对象的类用作地图中的键即可。这个带有Java流的例子就是这样的:

List<Cars> merged = Stream.of(dealer1, dealer2)
                    .flatMap(Collection::stream)
                    .collect( Collectors.toMap( Object::getClass, Function.identity(), (c1, c2) -> c1 ) )
                    .values()
                        .stream().collect( Collectors.toList() );