JavaRush /Blog Java /Random-VI /Топ 10 вопросов о коллекциях в Java
FedoraLinux
Mức độ
Москва

Топ 10 вопросов о коллекциях в Java

Xuất bản trong nhóm
Статья является переводом статьи "Top 10 questions about Java Collections". Ниже представлены самые популярные вопросы по поводу коллекций в Java, заданные и обсужденные на Stackowerflow. До того, How Вы посмотрите на эти вопросы, хорошо бы посмотреть диаграмму иерархии классов. 1. Когда использовать LinkedList instead of ArrayList? ArrayList по факту является массивом, его элементы могут быть доступны непосредственно по индексу. В случае переполнения массива появляется необходимость в новом, имеющем больше места. Размещение и перемещение всех элементов будет занимать O(n) времени. Также, необходимо добавление и удаление элементов для передвижения существующих элементов в массиве. Это, возможно, самое большое неудобство в использовании ArrayList. LinkedList - это двойной список ссылок на элементы. Таким образом, для доступа к элементу в центре, приходится производить поиск с самого начала и до конца листа. С другой стороны, добавление и удаление element в LinkedList быстрее по той причине, что эти операции лишь изменяют сам список. Самые плохие моменты времени сравниваются ниже:
Метод Arraylist LinkedList
get(index) O(1) O(n)
add(E) O(n) O(1)
add(E, index) O(n) O(n)
remove(index) O(n) O(n)
Iterator.remove() O(n) O(1)
Iterator.add(E) O(n) O(1)
Не смотря на время работы, использование памяти должно быть продумано индивидуально для больших списков. В LinkedList каждый узел должен иметь How минимум два дополнительных указателя, чтобы связывать предыдущий и следующий узлы, в то время, How в ArrayList нужен только массив элементов. Больше сравнений списков ArrayList, LinkedList и Vector (англ.). 2. Эффективный эквивалент для удаления элементов во время итерации коллекции Единственный корректный путь для модификации (удаления элементов) колекции во время итерации - это использование Iterator.remove(). Например: Iterator itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); } Самый распространенный вариант ошибки: for(Integer i: list) { list.remove(i); } Вы получите ConcurrentModificationException во время работы codeа выше. Так происходит по той причине, что итератор был сгенерирован для перемещения по всему списку, но в то же время лист изменяется вызовом Iterator.remove(). Как написано в documentации по этому исключению,
"it is not generally permissible for one thread to modify a collection while another thread is iterating over it."
Nói chung, không thể chấp nhận được một luồng sửa đổi một tập hợp trong khi một luồng khác đang duyệt qua nó. 3. Làm cách nào để chuyển List thành mảng int[]? Cách dễ nhất để làm điều này là sử dụng ArrayUtils , nằm trong thư viện Apache Commons Lang . int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0])); Không có lối tắt cho biểu thức này trong JDK. Hãy nhớ rằng bạn không thể sử dụng List.toArray() vì biểu thức này chuyển đổi List thành Integer[] (không phải là kiểu nguyên thủy). Cách chính xác sẽ là tùy chọn sau: int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); } 4. Làm cách nào để chuyển đổi một mảng int[] thành Danh sách? Cách dễ nhất cũng là sử dụng ArrayUtils trong thư viện Apache Commons Lang , như trên. List list = Arrays.asList(ArrayUtils.toObject(array)); Ngoài ra, không có lối tắt cho biểu thức này trong JDK. 5. Cách tốt nhất để lọc bộ sưu tập là gì? Bạn có thể sử dụng các gói của bên thứ ba như Guava hoặc Apache Commons Lang để tăng chức năng. Cả hai gói này đều có phương thức filter() (trong lớp Collections2 từ Guava và CollectionUtils từ Apache). Phương thức filter() sẽ trả về các phần tử khớp với Vị ngữ đã cho. Trong JDK mọi thứ phức tạp hơn. Tin vui là các biến vị ngữ sẽ được thêm vào trong Java 8 , nhưng hiện tại bạn cần sử dụng Iterator để lặp qua toàn bộ bộ sưu tập. Tất nhiên, bạn có thể bắt chước con đường mà Guava và Apache đã theo đuổi bằng cách làm quen với giao diện Vị ngữ mới. Bây giờ chúng ta có thể sử dụng đoạn mã sau để lọc bộ sưu tập: 6. Làm cách nào để dễ dàng chuyển đổi Danh sách thành Tập hợp? Có hai cách để làm điều này, tùy thuộc vào cách bạn muốn xác định sự bình đẳng. Đoạn mã đầu tiên đưa danh sách vào HashSet. Bản sao trong trường hợp này được xác định chủ yếu bởi hashCode(). Thông thường điều này sẽ hoạt động. Nhưng nếu bạn cần tính đến đường dẫn so sánh, thì tốt hơn nên sử dụng phần thứ hai của mã, nơi bạn có thể xác định bộ so sánh của riêng mình. 7. Làm cách nào để xóa các phần tử trùng lặp khỏi ArrayList? Câu hỏi này có phần liên quan đến câu hỏi trên. Nếu thứ tự của các phần tử trong ArrayList không quan trọng đối với bạn, thì một bước đi thông minh là đặt trang tính vào một Tập hợp để loại bỏ các phần tử trùng lặp, sau đó đưa nó trở lại Danh sách. Dưới đây là một ví dụ. Nếu thứ tự của các phần tử quan trọng đối với bạn thì thứ tự đó có thể được đảm bảo bằng cách đặt danh sách vào LinkedHashSet , nằm trong JDK tiêu chuẩn. 8. Sắp xếp bộ sưu tập int[] array = {1,2,3,4,5}; List list = new ArrayList (); for(int i: array) { list.add(i); } Iterator itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } } public interface Predicate { boolean test(T o); } public static void filter(Collection collection, Predicate predicate) { if ((collection != null) && (predicate != null)) { Iterator itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } } filter(list, new Predicate () { public boolean test(Integer i) { return i <= 5; } }); Set set = new HashSet (list); Set set = new TreeSet (aComparator); set.addAll(list); ArrayList** list = ... // initial a list with duplicate elements Set set = new HashSet (list); list.clear(); list.addAll(set); Có một số cách để hỗ trợ bộ sưu tập được sắp xếp trong Java. Tất cả chúng đều cung cấp một bộ sưu tập theo thứ tự tự nhiên hoặc bằng một bộ so sánh được chỉ định. Trong trường hợp theo thứ tự tự nhiên, bạn cũng cần triển khai giao diện Comparable trên phần tử.
  1. Collections.sort() có thể sắp xếp Danh sách. Như đã nêu trong tài liệu Java, kiểu sắp xếp này ổn định và đảm bảo hiệu suất n log(n).
  2. PriorityQueue cung cấp một hàng đợi có trật tự. Sự khác biệt giữa PriorityQueue và Collections.sort() là PriorityQueue luôn duy trì thứ tự của hàng đợi, nhưng bạn chỉ có thể lấy phần tử đầu tiên của hàng đợi. Bạn không thể truy cập ngẫu nhiên một phần tử như PriorityQueue.get(4).
  3. Nếu không có bản sao trong bộ sưu tập, bạn có thể chọn TreeSet . Cũng giống như PriorityQueue, TreeSet luôn duy trì một tập hợp có thứ tự. Bạn có thể lấy phần tử nhỏ nhất hoặc lớn nhất từ ​​TreeSet, nhưng bạn vẫn không thể có quyền truy cập ngẫu nhiên vào các phần tử đó.
Nói một cách đơn giản, Collections.sort() cung cấp danh sách được sắp xếp một lần. PriorityQueue và TreeSet luôn duy trì một bộ sưu tập có thứ tự, với cái giá phải trả là thiếu quyền truy cập được lập chỉ mục vào các phần tử. 9. Collections.emptyList() hoặc phiên bản mới Câu hỏi tương tự cũng áp dụng cho EmptyMap() và EmptySet(). Cả hai phương thức đều trả về một danh sách trống, nhưng Collections.emptyList() là một danh sách không thể thay đổi. Điều này có nghĩa là bạn không thể thêm phần tử mới vào danh sách "trống". Trong nền, mỗi lệnh gọi phương thức Collections.emptyList() không thực sự tạo ra một phiên bản mới của danh sách trống. Thay vào đó, nó sẽ sử dụng lại phiên bản trống hiện có. Nếu bạn đã quen thuộc với Singleton như một mẫu thiết kế , bạn nên hiểu ý nghĩa của nó. Điều này sẽ mang lại cho bạn hiệu suất tốt hơn nếu được gọi thường xuyên. 10 Sao chép một bộ sưu tập, Collections.copy() Có hai cách để sao chép danh sách nguồn sang danh sách đích. Một cách là sử dụng hàm tạo ArrayList. Một cách khác là sử dụng phương thức Collections.copy() . Lưu ý ở dòng đầu tiên: chúng tôi đang phân bổ một danh sách có độ dài ít nhất bằng độ dài của danh sách ban đầu, vì tài liệu Java về các bộ sưu tập cho biết: ArrayList dstList = new ArrayList (srcList);
Danh sách đích ít nhất phải dài bằng danh sách nguồn.
Điều đó có nghĩa là danh sách cuối cùng không được ngắn hơn danh sách ban đầu. Cả hai phương pháp đều là sao chép nông. Vậy sự khác biệt giữa hai phương pháp này là gì? Đầu tiên, Collections.copy() sẽ không phân bổ lại dung lượng bộ sưu tập của dstList, ngay cả khi dstList không có đủ không gian để chứa tất cả các phần tử từ srcList. Thay vào đó, nó sẽ ném ra ngoại lệ IndexOutOfBoundsException . Người ta có thể hỏi liệu điều này có lợi ích gì không. Lý do là điều này đảm bảo rằng phương thức chạy tuyến tính theo thời gian. Điều này cũng phù hợp khi bạn muốn sử dụng lại mảng thay vì phân bổ lại bộ nhớ trong hàm tạo ArrayList. Thay vì đưa ra kết luận Nếu sau khi đọc bài viết mà bạn vẫn còn thắc mắc, hãy hỏi họ trong phần bình luận. Ngoài ra, nếu bạn thấy bản dịch có sai sót hoặc sai sót nào khác thì hãy viết thư cho PM, nó sẽ được sửa và bạn sẽ được cảm ơn. Nguyên bản. ArrayList dstList = new ArrayList (srcList.size()); Collections.copy(dstList, srcList);
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION