Пересечение и объединение ArrayLists в Java
есть ли методы для этого? Я искал, но ничего не нашел.
еще один вопрос: мне нужны эти методы, чтобы я мог фильтровать файлы.
Некоторые из них AND
фильтры и некоторые OR
фильтры (как в теории множеств), поэтому мне нужно фильтровать по всем файлам и объединять/пересекать ArrayLists, который содержит эти файлы.
Я должен использовать другую структуру данных для хранения файлов? Есть ли что-нибудь еще, что бы предложить лучшее время выполнения?
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; } } } }
}