Пересечение и объединение ArrayLists в Java


есть ли методы для этого? Я искал, но ничего не нашел.

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

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

19 111

19 ответов:

вот простая реализация без использования какой-либо сторонней библиотеки. Главное преимущество перед 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;
    }
}

коллекция (так ArrayList также) есть:

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

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

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]

этот пост довольно старый, но тем не менее он был первым, кто появился в 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());

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

list1.retainAll(list2) - is intersection

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

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

объединения и пересечения, определенные только для наборов, а не списков. Как ты и говорил.

Регистрация гуавы библиотека для фильтров. Также гуава обеспечивает реальный пересечений и объединений

 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)

можно использовать CollectionUtils с Апач Коммонс.

отмеченное решение не является эффективным. Он имеет временную сложность 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++, I не знаю, верно ли это и в Java).

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

вот способ, как вы можете сделать пересечение с потоками (помните, что вы должны использовать 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());

пример для списков с различными типами. Если у вас есть realtion между 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());
  • retainAll изменит ваш список
  • Guava не имеет API для списка (только для набора)

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

используйте ListUtils из org.апаш.палата общин.коллекции, если вы не хотите изменять существующий список.

ListUtils.intersection(list1, list2)

в 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();
}

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

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;
    }
}

Я также работал над аналогичной ситуацией и добрался сюда в поисках помощи. В итоге я нашел свое собственное решение для массивов. ArrayList AbsentDates = new 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 );
        }

вы можете использовать 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]

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

//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;
}

во-первых, я копирую все значения массивов в один массив, а затем удаляю дубликаты значений в массив. Строка 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]+" ");
    }
}   
}

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

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

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

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

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));
}

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

если число соответствует, чем я проверяю, это происходит в первый раз или нет с помощью "indexOf()" если число соответствует в первый раз, то распечатать и сохранить в строку так, что когда в следующий раз тот же номер соответствует, то он не будет печатать, потому что из-за "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;
                }
           }
       }    
}

}