Как получить необычные объекты из двух списков различных объектов - PullRequest
0 голосов
/ 11 мая 2018

У меня есть два списка

List<Foo> foolist = getAll();
List<Bar> barList = getAll();

class Foo {
    String name;
    String age;
    ....
    // more fields
}

class Bar {
    String barName;
    int codeBar;
    ....
    // more field
}

Мы можем установить отношения между ними с возрастом <-> codeBar и именем <-> barName Я хочу получить объекты Бар, которые не в глупости, Как это можно сделать с помощью потоков?

Я вижу примеры использования stream (). Filter (list :: contains), но в этом случае он недопустим.

Кто-нибудь может указать мне правильное направление?

Спасибо всем. Я смотрю что-то вроде этого: barList.stream().filter(b->fooList.stream().anyMatch(f->!b.getBarName().equalsIgnoreCase(f.getName) && b.getCodeBar() == Integer.valueOf(age))).collect(Collectors.toList());

Я пока не знаю, правильно ли это

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Наивное решение состоит в том, чтобы пройти через оба списка.Предполагая, что вы можете сформулировать условие равенства как BiPredicate<Foo, Bar>:

for (Bar bar: bars) {
    boolean foundEqualFoo = false;
    for(Foo foo: foos) {
        if (predicate.test(foo, bar)) {
            foundEqualFoo = true;
            break;
        }
    }
    if (!foundEqualFoo) {
        barsNotInFoos.add(bar);
    }
}

Это O(n*m) сложность времени.

Лучшим решением было бы определить класс ключа, такой как FooBarKey, который быудерживайте name и age и правильно внедрите equals(...) и hashCode().Затем вы можете сделать следующее:

Map<FooBarKey, List<Bar>> barsByKey = bars
    .stream()
    .collect(Collectors.groupingBy(
        FooBarKey::ofBar,
        HashMap::new,
        Collectors.toList()));

Set<FooBarKey> fooKeys = foos
    .stream()
    .map(FooBarKey::ofFoo)
    .collect(Collectors.toCollection(HashSet::new));

barsByKey.keySet().removeAll(fooKeys);

Set<Bar> uncommonBars = barsByKey
    .values()
    .stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toSet());

Несколько замечаний по решению:

  • Я думаю, что здесь разумно создавать Map<FooBarKey, ...>, чтобы избежать постоянного создания новых экземпляров FooBarKey дляодни и те же Bar с.
  • необходима мультикарта Map<FooBarKey, List<Bar>>, поскольку в списке могут быть столбцы с одинаковым ключом. Поставщики
  • HashMap::new и HashSet::newиспользуется там, где это необходимо, чтобы убедиться, что хеш-таблицы используются для гарантии O(1) для соответствующих операций.

Это должно быть O(2*n + m).

0 голосов
/ 11 мая 2018

Вы можете сделать что-то вроде этого:

public static void main(String[] args) {
    List<Foo> fooList = asList(new Foo("1", "1"), new Foo("2", "2"), new Foo("3", "3"));
    List<Bar> barList = asList(new Bar("4", 4), new Bar("3", 3), new Bar("5", 5));

    Set<Blah> fooSet = fooList.stream().map(Main::fooToBlah).collect(toCollection(HashSet::new));
    Set<Blah> barSet = barList.stream().map(Main::barToBlah).collect(toCollection(HashSet::new));

    barList.stream()
            .filter(bar -> !fooSet.contains(barToBlah(bar)))
            .forEach(System.out::println);
    fooList.stream()
            .filter(foo -> !barSet.contains(fooToBlah(foo)))
            .forEach(System.out::println);
}

static Blah fooToBlah(Foo foo) {
    return new Blah(foo.name, foo.age);
}

static Blah barToBlah(Bar bar) {
    return new Blah(bar.barName, "" + bar.codeBar);
}

static class Blah {
    String name;
    String age;

    public Blah(String name, String age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }
}
  1. Возьмите все Foo объекты и преобразуйте их в Blah (конечно, назовите его соответствующим образом) - это необходимо, потому что Foo имеет другие поля, которые нам не нужны.
  2. Поместите Blah объектов в HashSet, чтобы у вас не было O(n*m) сложности со временем. Вы могли бы сделать просто collect(toSet()), но я предпочитаю быть прямо об этом здесь, так как это важно для производительности.
  3. Перейдите к Bar объектам, и если объект не входит в вышеуказанный набор, это необычный Bar объект. Его необходимо преобразовать в Blah, и, к сожалению, вы не можете использовать Stream.map, так как в конце оно все равно должно быть Stream<Bar>, а не Stream<Blah>.
  4. Повторите выше три шага для другого списка, чтобы найти все необычные Foo объекты.

Помните, что в классе Blah необходимы методы equals и hashCode. Foo и Bar классы не нуждаются в них.


EDIT:

Вы можете преобразовать следующий код

Set<Blah> fooSet = fooList.stream().map(Main::fooToBlah).collect(toCollection(HashSet::new));

barList.stream()
        .filter(bar -> !fooSet.contains(barToBlah(bar)))
        .forEach(System.out::println);

примерно так

barList.stream()
        .filter(bar -> fooList.stream()
                .map(Main::fooToBlah)
                .noneMatch(foo -> foo.equals(barToBlah(bar)))
        )
        .forEach(System.out::println);

или даже полностью удалить Blah класс

barList.stream()
        .filter(bar -> fooList.stream().noneMatch(foo -> Objects.equals(foo.name, bar.barName) && Objects.equals(foo.age, "" + bar.codeBar)
        .forEach(System.out::println);

но у вас будет намного хуже сложность времени, а также возникает вопрос читабельности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...