Пересечение и объединение списков массивов в Java

130

Есть какие-то способы сделать это? Я искал, но не нашел.

Другой вопрос: мне нужны эти методы, чтобы можно было фильтровать файлы. Некоторые из них являются ANDфильтрами, а некоторые - ORфильтрами (например, в теории множеств), поэтому мне нужно фильтровать по всем файлам, и объединить / пересечь списки ArrayLists, которые содержат эти файлы.

Следует ли мне использовать другую структуру данных для хранения файлов? Есть ли что-нибудь еще, что могло бы улучшить время выполнения?

yotamoo
источник
1
Если вы не хотите создавать новый список, Vector.retainAll (Vector) обрезает ваш исходный вектор только до пересечения со вторым вектором.
user2808054
@ user2808054 почему Vector? Этот класс не поощрялся начиная с Java 1.2.
dimo414
@ dimo414 интерфейс, который я использую (у меня нет выбора), возвращает вещи как векторы. Я не знал, что это разочаровало! Спасибо за информацию .. Кто разочарован? Я не видел никаких заметок о том, что он устарел, поэтому это сюрприз
user2808054 01
1
Из документации Javadocs: « Начиная с платформы Java 2 v1.2 ... рекомендуется использовать ArrayList вместо Vector. ». Единственный раз, когда вам может понадобиться, Vector- это взаимодействие между потоками, но есть и более безопасные структуры данных для этих случаев использования. См. Также этот вопрос . На Vectorмой взгляд, любые библиотеки, которые все еще используются в 2016 году, вызывают большие подозрения.
dimo414 01
@ dimo414, это библиотека IBM, ха-ха! (API данных Lotus Domino). Спасибо за информацию, очень полезно
user2808054 08

Ответы:

122

Вот простая реализация без использования сторонней библиотеки. Основное преимущество перед retainAll, removeAllи в addAllтом , что эти методы не изменяют первоначальные списки вклада методов.

public class Test {

    public static void main(String... args) throws Exception {

        List<String> list1 = new ArrayList<String>(Arrays.asList("A", "B", "C"));
        List<String> list2 = new ArrayList<String>(Arrays.asList("B", "C", "D", "E", "F"));

        System.out.println(new Test().intersection(list1, list2));
        System.out.println(new Test().union(list1, list2));
    }

    public <T> List<T> union(List<T> list1, List<T> list2) {
        Set<T> set = new HashSet<T>();

        set.addAll(list1);
        set.addAll(list2);

        return new ArrayList<T>(set);
    }

    public <T> List<T> intersection(List<T> list1, List<T> list2) {
        List<T> list = new ArrayList<T>();

        for (T t : list1) {
            if(list2.contains(t)) {
                list.add(t);
            }
        }

        return list;
    }
}
adarshr
источник
16
вы можете создать новый список с элементами list1, а затем вызвать методы keepAll и addAll
lukastymo
почему вы используете strictfp в этом решении?
lukastymo
9
Следует использовать HashSetдля, intersectionчтобы средняя производительность для случая была O (n) вместо O (n ^ 2).
Zong
1
В этом посте можно использовать обновление, чтобы продемонстрировать преимущества Java 8 Stream API.
SME_Dev 02
Я получаю сообщение об ошибке Когда пытаюсь присвоить это значение -> Пример: ArrayList <String> total total = (ArrayList <String>) пересечение (list2, list1) ---> не удается преобразовать java.util.arraylist в java.util.arraylist < строка>
доставка
123

Коллекция (так что и ArrayList) имеет:

col.retainAll(otherCol) // for intersection
col.addAll(otherCol) // for union

Используйте реализацию List, если вы принимаете повторы, и реализацию Set, если вы этого не сделаете:

Collection<String> col1 = new ArrayList<String>(); // {a, b, c}
// Collection<String> col1 = new TreeSet<String>();
col1.add("a");
col1.add("b");
col1.add("c");

Collection<String> col2 = new ArrayList<String>(); // {b, c, d, e}
// Collection<String> col2 = new TreeSet<String>();
col2.add("b");
col2.add("c");
col2.add("d");
col2.add("e");

col1.addAll(col2);
System.out.println(col1); 
//output for ArrayList: [a, b, c, b, c, d, e]
//output for TreeSet: [a, b, c, d, e]
lukastymo
источник
3
Было предложено отредактировать это объединение «неверно, поскольку оно будет содержать общие элементы дважды» . Редакция рекомендовала HashSetвместо этого использовать .
Кос,
5
Фактически он был отредактирован, см .: «Используйте реализацию List, если вы принимаете повторы, и реализацию Set, если вы этого не сделаете:»
lukastymo
7
Нет, keepAll не является пересечением для списка. Выше все элементы в col, которых нет в otherCol, удаляются. Допустим, otherCol - это {a, b, b, c}, а col - это {b, b, b, c, d}. Тогда col заканчивается {b, b, b, c}, что не является точным пересечением этих двух. Я ожидал, что это будет {b, b, c}. Выполняется другая операция.
demongolem
1
Я также не понимаю, как addAll()происходит объединение списков; он просто присоединяет второй список к концу первого. Операция объединения позволит избежать добавления элемента, если первый список уже содержит его.
dimo414
66

Этот пост довольно старый, но тем не менее он был первым, который появился в Google при поиске этой темы.

Я хочу дать обновление, используя потоки Java 8, выполняющие (в основном) то же самое в одной строке:

List<T> intersect = list1.stream()
    .filter(list2::contains)
    .collect(Collectors.toList());

List<T> union = Stream.concat(list1.stream(), list2.stream())
    .distinct()
    .collect(Collectors.toList());

Если у кого-то есть лучшее / более быстрое решение, дайте мне знать, но это решение - хороший лайнер, который можно легко включить в метод, не добавляя ненужный вспомогательный класс / метод, при этом сохраняя читабельность.

Fat_FS
источник
19
Ооо, это может быть хороший однострочник, но он занимает время O (n ^ 2). Преобразуйте один из списков в, а Setзатем используйте метод набора contains. Не все в жизни нужно делать потоками.
dimo414
31
list1.retainAll(list2) - is intersection

союз будет removeAllи тогдаaddAll .

Дополнительные сведения см. В документации коллекции (ArrayList - это коллекция) http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html

ГиГ
источник
1
Оба retainAll()и removeAll()являются операциями O (n ^ 2) над списками. Мы можем сделать лучше.
dimo414
1
Я проголосовал за, но теперь у меня есть вопрос. retainAllиз {1, 2, 2, 3, 4, 5} по {1, 2, 3} приводит к {1, 2, 2, 3}. Разве это не должно быть перекрестком {1, 2, 3}?
GyuHyeon Choi
21

Союзы и пересечения определены только для множеств, а не списков. Как вы упомянули.

Проверьте библиотеку guava на наличие фильтров. Также гуава обеспечивает реальные пересечения и союзы

 static <E> Sets.SetView<E >union(Set<? extends E> set1, Set<? extends E> set2)
 static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)
Стэн Курилин
источник
12

Вы можете использовать CollectionUtilsс Apache Commons .

bluefoot
источник
7
Если кто-то сочтет этот ответ слишком коротким: методы CollectionUtils.containsAny и CollectionUtils.containsAll.
Себастьян
2
странно, что CollectionUtils из apache commons не поддерживает дженерики
Василий Саржинский
7

Отмеченное решение неэффективно. Он имеет временную сложность O (n ^ 2). Что мы можем сделать, так это отсортировать оба списка и выполнить алгоритм пересечения, как показано ниже.

private  static ArrayList<Integer> interesect(ArrayList<Integer> f, ArrayList<Integer> s) { 
    ArrayList<Integer> res = new ArrayList<Integer>();

    int i = 0, j = 0; 
    while (i != f.size() && j != s.size()) { 

        if (f.get(i) < s.get(j)) {
            i ++;
        } else if (f.get(i) > s.get(j)) { 
            j ++;
        } else { 
            res.add(f.get(i)); 
            i ++;  j ++;
        }
    }


    return res; 
}

У этого есть сложность O (n log n + n), которая находится в O (n log n). Аналогичным образом производится объединение. Просто убедитесь, что вы внесли подходящие изменения в операторы if-elseif-else.

Вы также можете использовать итераторы, если хотите (я знаю, что они более эффективны в C ++, я не знаю, верно ли это и в Java).

AJed
источник
1
Недостаточно общий, T может не быть Сопоставимым, а в некоторых случаях сравнение стоит дорого ...
Борис Чурзин
Не универсальный, я полностью согласен. Сравнение дорого? как бы вы это решили?
AJed
К сожалению, дешевле было бы сделать это за O (n ^ 2) :) Для Numbers это решение хорошее ...
Борис Чурзин
К сожалению, вы не ответили на мой вопрос. Позвольте мне перефразировать, как O (n ^ 2) лучше с учетом функции сравнения стоимости c (n)?
AJed
1
Преобразование одного ввода в набор и вызов contains()в цикле (как предлагает Devenv) займет время O (n + m). Сортировка излишне сложна и занимает O (n log n + m log n + n) времени. Конечно, это сокращает время до O (n log n), но это все еще хуже, чем линейное время, и намного сложнее.
dimo414
4

Я думаю, вам следует использовать a Setдля хранения файлов, если вы хотите пересечь и объединить их. Затем вы можете использовать гуавы «s Устанавливает класс делать union, intersectionи фильтрацию по Predicateа. Разница между этими методами и другими предложениями заключается в том, что все эти методы создают ленивые представления объединения, пересечения и т. Д. Двух наборов. Apache Commons создает новую коллекцию и копирует в нее данные. retainAllизменяет одну из ваших коллекций, удаляя из нее элементы.

ColinD
источник
4

Вот способ пересечения потоков (помните, что для потоков нужно использовать java 8):

List<foo> fooList1 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<foo> fooList2 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
fooList1.stream().filter(f -> fooList2.contains(f)).collect(Collectors.toList());

Пример для списков разных типов. Если у вас есть связь между foo и bar и вы можете получить объект bar из foo, чем вы можете изменить свой поток:

List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));

fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());
Deutro
источник
3
  • keepAll изменит ваш список
  • У Guava нет API для списка (только для набора)

Я нашел ListUtils очень полезным для этого случая использования.

Используйте ListUtils из org.apache.commons.collections, если вы не хотите изменять существующий список.

ListUtils.intersection(list1, list2)

Bala
источник
3

Вы можете использовать commons-collections4 CollectionUtils

Collection<Integer> collection1 = Arrays.asList(1, 2, 4, 5, 7, 8);
Collection<Integer> collection2 = Arrays.asList(2, 3, 4, 6, 8);

Collection<Integer> intersection = CollectionUtils.intersection(collection1, collection2);
System.out.println(intersection); // [2, 4, 8]

Collection<Integer> union = CollectionUtils.union(collection1, collection2);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]

Collection<Integer> subtract = CollectionUtils.subtract(collection1, collection2);
System.out.println(subtract); // [1, 5, 7]
xxg
источник
2

В Java 8 я использую простые вспомогательные методы вроде этого:

public static <T> Collection<T> getIntersection(Collection<T> coll1, Collection<T> coll2){
    return Stream.concat(coll1.stream(), coll2.stream())
            .filter(coll1::contains)
            .filter(coll2::contains)
            .collect(Collectors.toSet());
}

public static <T> Collection<T> getMinus(Collection<T> coll1, Collection<T> coll2){
    return coll1.stream().filter(not(coll2::contains)).collect(Collectors.toSet());
}

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}
Pascalius
источник
1

Если объекты в списке являются хешируемыми (т.е. имеют приличный hashCode и функцию равенства), самый быстрый подход между таблицами прибл. size> 20 - это создать HashSet для большего из двух списков.

public static <T> ArrayList<T> intersection(Collection<T> a, Collection<T> b) {
    if (b.size() > a.size()) {
        return intersection(b, a);
    } else {
        if (b.size() > 20 && !(a instanceof HashSet)) {
            a = new HashSet(a);
        }
        ArrayList<T> result = new ArrayList();
        for (T objb : b) {
            if (a.contains(objb)) {
                result.add(objb);
            }
        }
        return result;
    }
}
Йерун Вууренс
источник
1

Я также работал над подобной ситуацией и обратился сюда за помощью. В итоге я нашел собственное решение для массивов. ArrayList AbsentDates = новый ArrayList (); // Сохраним Array1-Array2

Примечание. Отправьте это сообщение, если это может помочь кому-то обратиться за помощью к этой странице.

ArrayList<String> AbsentDates = new ArrayList<String>();//This Array will store difference
      public void AbsentDays() {
            findDates("April", "2017");//Array one with dates in Month April 2017
            findPresentDays();//Array two carrying some dates which are subset of Dates in Month April 2017

            for (int i = 0; i < Dates.size(); i++) {

                for (int j = 0; j < PresentDates.size(); j++) {

                    if (Dates.get(i).equals(PresentDates.get(j))) {

                        Dates.remove(i);
                    }               

                }              
                AbsentDates = Dates;   
            }
            System.out.println(AbsentDates );
        }
Шубхам Панди
источник
1

Пересечение двух списков разных объектов на основе общего ключа - Java 8

 private List<User> intersection(List<User> users, List<OtherUser> list) {

        return list.stream()
                .flatMap(OtherUser -> users.stream()
                        .filter(user -> user.getId()
                                .equalsIgnoreCase(OtherUser.getId())))
                .collect(Collectors.toList());
    }
Нирадж Сонаване
источник
как насчет разницы между этими двумя списками?
jean
1
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
    Set<T> set1, set2;
    if (col1 instanceof Set) {
        set1 = (Set) col1;
    } else {
        set1 = new HashSet<>(col1);
    }

    if (col2 instanceof Set) {
        set2 = (Set) col2;
    } else {
        set2 = new HashSet<>(col2);
    }

    Set<T> intersection = new HashSet<>(Math.min(set1.size(), set2.size()));

    for (T t : set1) {
        if (set2.contains(t)) {
            intersection.add(t);
        }
    }

    return intersection;
}

JDK8 + (вероятно, лучшая производительность)

public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
    boolean isCol1Larger = col1.size() > col2.size();
    Set<T> largerSet;
    Collection<T> smallerCol;

    if (isCol1Larger) {
        if (col1 instanceof Set) {
            largerSet = (Set<T>) col1;
        } else {
            largerSet = new HashSet<>(col1);
        }
        smallerCol = col2;
    } else {
        if (col2 instanceof Set) {
            largerSet = (Set<T>) col2;
        } else {
            largerSet = new HashSet<>(col2);
        }
        smallerCol = col1;
    }

    return smallerCol.stream()
            .filter(largerSet::contains)
            .collect(Collectors.toSet());
}

Если вам не важна производительность и вы предпочитаете меньший код, просто используйте:

col1.stream().filter(col2::contains).collect(Collectors.toList());
Исмаил Явуз
источник
0

Окончательное решение:

//all sorted items from both
public <T> List<T> getListReunion(List<T> list1, List<T> list2) {
    Set<T> set = new HashSet<T>();
    set.addAll(list1);
    set.addAll(list2);
    return new ArrayList<T>(set);
}

//common items from both
public <T> List<T> getListIntersection(List<T> list1, List<T> list2) {
    list1.retainAll(list2);
    return list1;
}

//common items from list1 not present in list2
public <T> List<T> getListDifference(List<T> list1, List<T> list2) {
    list1.removeAll(list2);
    return list1;
}
Choletski
источник
0

Сначала я копирую все значения массивов в один массив, а затем удаляю повторяющиеся значения в массив. Строка 12, объясняющая, если одно и то же число встречается чаще, чем раз, то помещает дополнительное мусорное значение в позицию «j». В конце перейдите от начала к концу и проверьте, возникает ли такое же значение мусора, а затем отбросьте.

public class Union {
public static void main(String[] args){

    int arr1[]={1,3,3,2,4,2,3,3,5,2,1,99};
    int arr2[]={1,3,2,1,3,2,4,6,3,4};
    int arr3[]=new int[arr1.length+arr2.length];

    for(int i=0;i<arr1.length;i++)
        arr3[i]=arr1[i];

    for(int i=0;i<arr2.length;i++)
        arr3[arr1.length+i]=arr2[i];
    System.out.println(Arrays.toString(arr3));

    for(int i=0;i<arr3.length;i++)
    {
        for(int j=i+1;j<arr3.length;j++)
        {
            if(arr3[i]==arr3[j])
                arr3[j]=99999999;          //line  12
        }
    }
    for(int i=0;i<arr3.length;i++)
    {
        if(arr3[i]!=99999999)
            System.out.print(arr3[i]+" ");
    }
}   
}
Ashutosh
источник
1
Добро пожаловать в стек переполнения! Обратите внимание, что речь идет о ArrayList. Кроме того, я боюсь, что эта конкретная реализация оставляет желать лучшего. Во вводе может встречаться значение 99999999, которое используется в качестве дозорного. Лучше было бы использовать динамическую структуру, например ArrayList, для хранения результата объединения.
SL Barth - Reinstate Monica
1
Пожалуйста, объясните код, который вы представили, а не просто кодовый ответ.
tmarois 07
Я просто даю подсказку, что вы должны
Ашутош
Рад видеть, что вы добавили объяснение. К сожалению, сам ответ по-прежнему плохой. Нет смысла использовать массивы. Вам следует использовать динамическую структуру, например ArrayList. Если (по какой-то причине) вы должны использовать массивы, вам следует подумать об использовании массива, Integerа не int. Тогда вы можете использовать nullвместо своего «мусорное значение». «Мусорные значения» или «контрольные значения» обычно плохая идея, потому что эти значения могут все еще присутствовать во входных данных.
SL Barth - Reinstate Monica
0

После тестирования вот мой лучший подход к пересечению.

Более высокая скорость по сравнению с чистым подходом HashSet. Приведенные ниже HashSet и HashMap имеют одинаковую производительность для массивов с более чем 1 миллионом записей.

Что касается подхода Java 8 Stream, скорость довольно низкая для массива размером более 10 КБ.

Надеюсь, это поможет.

public static List<String> hashMapIntersection(List<String> target, List<String> support) {
    List<String> r = new ArrayList<String>();
    Map<String, Integer> map = new HashMap<String, Integer>();
    for (String s : support) {
        map.put(s, 0);
    }
    for (String s : target) {
        if (map.containsKey(s)) {
            r.add(s);
        }
    }
    return r;
}
public static List<String> hashSetIntersection(List<String> a, List<String> b) {
    Long start = System.currentTimeMillis();

    List<String> r = new ArrayList<String>();
    Set<String> set = new HashSet<String>(b);

    for (String s : a) {
        if (set.contains(s)) {
            r.add(s);
        }
    }
    print("intersection:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
    return r;
}

public static void union(List<String> a, List<String> b) {
    Long start = System.currentTimeMillis();
    Set<String> r= new HashSet<String>(a);
    r.addAll(b);
    print("union:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
}
Dilabeing
источник
0

Метод keepAll () используется для поиска общего элемента. т.е .; пересечение list1.retainAll (list2)

ямини шрештха
источник
-1

Если у вас есть данные в наборах, вы можете использовать Setsкласс Guava .

Нил
источник
-1

Если число совпадает, чем я проверяю, это происходит в первый раз или нет с помощью "indexOf ()", если число совпадает в первый раз, затем распечатайте и сохраните в строке, чтобы при следующем совпадении того же числа оно было выиграно ' t печать, потому что из-за "indexOf ()" условие будет ложным.

class Intersection
{
public static void main(String[] args)
 {
  String s="";
    int[] array1 = {1, 2, 5, 5, 8, 9, 7,2,3512451,4,4,5 ,10};
    int[] array2 = {1, 0, 6, 15, 6, 5,4, 1,7, 0,5,4,5,2,3,8,5,3512451};


       for (int i = 0; i < array1.length; i++)
       {
           for (int j = 0; j < array2.length; j++)
           {
               char c=(char)(array1[i]);
               if(array1[i] == (array2[j])&&s.indexOf(c)==-1)
               {    
                System.out.println("Common element is : "+(array1[i]));
                s+=c;
                }
           }
       }    
}

}

Ashutosh
источник
2
Не просто отправляйте код в качестве ответа, дайте небольшое объяснение того, что вы делаете,
Брэндон Замудио,
это моя первая программа, которую я загрузил
Ашутош
2
Хотя этот код может помочь решить проблему, он не объясняет, почему и / или как он отвечает на вопрос. Предоставление этого дополнительного контекста значительно улучшило бы его долгосрочную ценность. Пожалуйста , измените свой ответ , чтобы добавить объяснение, в том числе то , что применять ограничения и допущения.
Тоби Спейт