Java 集合一口气讲完!(上)||o(*°▽°*)o|Ю [有人吗?]
Java 集合遍历
Java集合教程 - Java集合遍历
在Java Collections Framework中,不同类型的集合使用不同类型的数据结构以不同的方式存储它们的元素。
一些集合对它们的元素有排序,有些没有。集合框架提供了遍历集合的以下方法:
- 使用迭代器
- 使用for-each循环
- 使用forEach()方法
使用迭代器
集合提供了一个迭代器来遍历其所有元素。
迭代器可以对集合执行以下三个操作:
- 检查是否有尚未访问的元素。
- 访问集合中的下一个元素。
- 删除集合的最后访问元素。
Java中的迭代器是 Iterator< E>
接口的一个实例。
我们可以使用Collection接口中的iterator()方法获取集合的迭代器。
以下代码创建一个字符串列表,并获取列表的迭代器:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Main { public static void main(String[] args) { // Create a list of strings List<String> names = new ArrayList<>(); // Get an iterator for the list Iterator<String> nameIterator = names.iterator(); } }
迭代器< E> 接口包含以下方法:
boolean hasNext() E next() default void remove() default void forEachRemaining(Consumer<? super E> action)
如果集合中有更多元素要迭代, hasNext()
方法将返回true。否则,它返回false。
next()
方法返回集合中的下一个元素。我们应该在调用 next()
方法之前调用 hasNext()
方法。如果没有, next()
方法会抛出NoSuchElementException异常。
例子
通常, hasNext()
和 next()
方法在循环中一起使用。以下代码使用迭代器打印列表的所有元素:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Main { public static void main(String[] args) { // Create a list of strings List<String> names = new ArrayList<>(); names.add("A"); names.add("B"); names.add("C"); // Get an iterator for the list Iterator<String> nameIterator = names.iterator(); // Iterate over all elements in the list while (nameIterator.hasNext()) { // Get the next element from the list String name = nameIterator.next(); System.out.println(name); } } }
上面的代码生成以下结果。
remove()
方法删除 next()
方法最后返回的元素。每次调用next()方法只能调用一次 remove()
方法。
如果对于每个 next()
方法或在第一次调用next()之前被多次调用 remove()
方法,它会抛出一个 IllegalStateException
异常。
对 remove()
方法的支持是可选的。remove()
方法可能会抛出一个 UnsupportedOperationException
异常。
例2
以下代码使用迭代器遍历列表的所有元素,并使用remove()方法删除该元素。
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Main { public static void main(String[] args) { // Create a list of strings List<String> names = new ArrayList<>(); names.add("A"); names.add("B"); names.add("C"); Iterator<String> nameIterator = names.iterator(); // Iterate over all elements in the list while (nameIterator.hasNext()) { // Get the next element from the list String name = nameIterator.next(); System.out.println(name); nameIterator.remove(); } System.out.println(names); } }
上面的代码生成以下结果。
例3
forEachRemaining()方法对集合中尚未由迭代器访问的每个元素执行操作。
action指定为 Consumer
。
以下代码显示如何打印列表的所有元素。
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Main { public static void main(String[] args) { // Create a list of strings List<String> names = new ArrayList<>(); names.add("A"); names.add("B"); names.add("C"); Iterator<String> nameIterator = names.iterator(); nameIterator.forEachRemaining(System.out::println); } }
上面的代码生成以下结果。
迭代器注意事项
迭代器是一次性对象。我们不能重置迭代器,它不能被重用。
要再次遍历同一集合的元素,请通过调用集合的iterator()方法来创建一个新的Iterator。
使用for-each循环
我们可以使用for-each循环遍历集合的元素。
我们可以使用for-each循环遍历任何实现类实现Iterable接口的集合。
for-each循环的一般语法如下:
Collection<T> yourCollection = ; for(T element : yourCollection) { }
在幕后,for-each循环获取迭代器并调用hasNext()和next()方法。
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Create a list of strings List<String> names = new ArrayList<>(); names.add("A"); names.add("B"); names.add("C"); for (String name : names) { System.out.println(name); } } }
上面的代码生成以下结果。
for-each注意事项
for-each循环有几个限制。
我们不能使用for-each循环从集合中删除元素。
以下代码将抛出ConcurrentModificationException异常:
List<String> names = get a list; for(String name : names) { names.remove(name);// Throws a ConcurrentModificationException }
对于for-each循环,我们没有办法从集合的中间开始。
for-each循环不提供访问先前访问的元素的方式。
使用forEach()方法
Iterable接口包含一个新的 forEach(Consumer action)
方法。
该方法遍历所有元素并应用操作。
forEach()
方法在从 Collection
接口继承的所有集合类型中都可用。
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Create a list of strings List<String> names = new ArrayList<>(); names.add("A"); names.add("B"); names.add("C"); names.forEach(System.out::println); } }
上面的代码生成以下结果。
Java Set集合
Java集合教程 - Java Set集合
Set 表示唯一对象的集合。集合中元素的排序是不相关的。
集合框架提供三种类型的集合:
- 数学集
- 排序集
- 导航集
数学集
Set
接口对数学中的一组进行建模。集合是唯一元素的集合。
Java最多允许一个Set中的一个空元素。 Set
中元素的排序并不重要。
Java不保证 Set
中元素的排序。
当循环遍历 Set
的所有元素时,你得到 Set
中的每个元素一次。
集合框架提供 HashSet
类作为实现为设置
接口。
以下代码显示了如何创建一个Set并向其添加元素。 当向集合添加重复元素时,它们将被忽略。
如果比较它们,则在集合中的两个元素被认为是相等的使用 equals()
方法返回true。
import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<String> s1 = new HashSet<>(); // Add a few elements s1.add("HTML"); s1.add("CSS"); s1.add("XML"); s1.add("XML"); // Duplicate // Create another set by copying s1 Set<String> s2 = new HashSet<>(s1); // Add a few more elements s2.add("Java"); s2.add("SQL"); s2.add(null); // one null is fine s2.add(null); // Duplicate System.out.println("s1: " + s1); System.out.println("s1.size(): " + s1.size()); System.out.println("s2: " + s2); System.out.println("s2.size(): " + s2.size()); } }
上面的代码生成以下结果。
LinkedHashSet
集合框架提供 LinkedHashSet
类作为 Set
接口的另一个实现类。
HashSet
不保证顺序元素。 LinkedHashSet
在插入元素时保持元素顺序。
import java.util.LinkedHashSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<String> s1 = new LinkedHashSet<>(); s1.add("A"); s1.add("B"); s1.add("C"); s1.add("D"); System.out.println("LinkedHashSet: " + s1); } }
上面的代码生成以下结果。
集合操作
我们可以对集合执行并集,交集和差分运算。
// Union of s1 and s2 will be stored in s1 s1.add(s2); // Intersection of s1 and s2 will be stored in s1 s1.retainAll(s2); // Difference of s1 and s2 will be stored in s1 s1.removeAll(s2);
在集合操作期间,修改s1。要保持原始设置不变,请在操作之前复制:
Set s1Unions2 = new HashSet(s1); // Make a copy of s1 s1Unions2.addAll(s2);
要测试集合s1是否是另一个集合s2的子集,请使用s2.containsAll(s1)方法。
import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<String> s1 = new HashSet<>(); s1.add("HTML"); s1.add("CSS"); s1.add("XML"); Set<String> s2 = new HashSet<>(); s2.add("Java"); s2.add("Javascript"); s2.add("CSS"); System.out.println("s1: " + s1); System.out.println("s2: " + s2); performUnion(s1, s2); performIntersection(s1, s2); performDifference(s1, s2); testForSubset(s1, s2); } public static void performUnion(Set<String> s1, Set<String> s2) { Set<String> s1Unions2 = new HashSet<>(s1); s1Unions2.addAll(s2); System.out.println("s1 union s2: " + s1Unions2); } public static void performIntersection(Set<String> s1, Set<String> s2) { Set<String> s1Intersections2 = new HashSet<>(s1); s1Intersections2.retainAll(s2); System.out.println("s1 intersection s2: " + s1Intersections2); } public static void performDifference(Set<String> s1, Set<String> s2) { Set<String> s1Differences2 = new HashSet<>(s1); s1Differences2.removeAll(s2); Set<String> s2Differences1 = new HashSet<>(s2); s2Differences1.removeAll(s1); System.out.println("s1 difference s2: " + s1Differences2); System.out.println("s2 difference s1: " + s2Differences1); } public static void testForSubset(Set<String> s1, Set<String> s2) { System.out.println("s2 is subset s1: " + s1.containsAll(s2)); System.out.println("s1 is subset s2: " + s2.containsAll(s1)); } }
上面的代码生成以下结果。
Java 排序集
Java集合教程 - Java排序集
排序集是在其元素上有排序的集合。
SortedSet
接口表示Java集合中的排序集合框架。
排序集中的元素可以按照自然顺序排序可比较的
接口或使用 Comparator
。
SortedSet
必须知道如何在添加元素时对其元素进行排序检查两个接口:
- 如果它的元素实现了Comparable接口,它将使用compareTo()方法来排序项目。 我们可以称之为自然顺序排序。
- 我们可以传递一个比较器做自定义排序。
如果指定了 Comparator
,则 Comparator
是用于排序并忽略 Comparable
接口。
TreeSet
类是Collection框架中SortedSet接口的一个实现。
例子
在下面的代码中,我们添加 String
对象 SortedSet
。
String
类实现 Comparable
接口。
SortedSet将使用 Comparable
接口及其 compareTo()
方法对String值进行排序。
import java.util.SortedSet; import java.util.TreeSet; public class Main { public static void main(String[] args) { // Create a sorted set of some names SortedSet<String> sortedNames = new TreeSet<>(); sortedNames.add("Java"); sortedNames.add("SQL"); sortedNames.add("HTML"); sortedNames.add("CSS"); // Print the sorted set of names System.out.println(sortedNames); } }
上面的代码生成以下结果。
例2
以下代码显示如何存储在 SortedSet
中的人物对象列表。
我们不能添加Person类的对象在SortedSet中,除非我们还提供一个 Comparator
对象因为Person类不实现 Comparable
接口。
以下代码创建一个 SortedSet
的使用 Comparator
的人使用他们的名字排序的人:
SortedSet<Person> personsSortedByName = new TreeSet<>(Comparator.comparing(Person::getName));
该代码使用方法引用来创建用于创建Comparator对象的lambda表达式。
import java.util.Comparator; import java.util.SortedSet; import java.util.TreeSet; public class Main { public static void main(String[] args) { SortedSet<Person> personsById = new TreeSet<>( Comparator.comparing(Person::getId)); personsById.add(new Person(1, "X")); personsById.add(new Person(2, "Z")); personsById.add(new Person(3, "A")); personsById.add(new Person(4, "C")); personsById.add(new Person(4, "S")); // A duplicate Person System.out.println("Persons by Id:"); personsById.forEach(System.out::println); SortedSet<Person> personsByName = new TreeSet<>( Comparator.comparing(Person::getName)); personsByName.add(new Person(1, "X")); personsByName.add(new Person(2, "Z")); personsByName.add(new Person(3, "A")); personsByName.add(new Person(4, "C")); System.out.println("Persons by Name: "); personsByName.forEach(System.out::println); } } class Person { private int id; private String name; public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (!(o instanceof Person)) { return false; } // id must be the same for two Persons to be equal Person p = (Person) o; if (this.id == p.getId()) { return true; } return false; } @Override public int hashCode() { return this.id; } @Override public String toString() { return "(" + id + ", " + name + ")"; } }
上面的代码生成以下结果。
例3
SortedSet
接口继承了 Set
接口的所有方法,并添加了一些方法来返回子集。
subSet(E fromElement,E toElement)
方法从 SortedSet
返回fromElement(包含)和toElement(exclusive)之间的元素。
import java.util.SortedSet; import java.util.TreeSet; public class Main { public static void main(String[] args) { SortedSet<String> names = new TreeSet<>(); names.add("HTML"); names.add("Java"); names.add("SQL"); names.add("CSS"); System.out.println("Sorted Set: " + names); System.out.println("First: " + names.first()); System.out.println("Last: " + names.last()); SortedSet<String> ssBeforeCSS = names.headSet("CSS"); System.out.println(ssBeforeCSS); SortedSet<String> ssBetwenCSSAndHTML = names.subSet("CSS", "HTML"); System.out.println(ssBetwenCSSAndHTML); SortedSet<String> ssBetwenCSSAndHTML2 = names.subSet("CSS", "HTML"); System.out.println(ssBetwenCSSAndHTML2); SortedSet<String> ssCSSAndAfter = names.tailSet("CSS"); System.out.println(ssCSSAndAfter); } }
上面的代码生成以下结果。
例4
以下代码片段使用 Comparator
创建一个 SortedSet
,它将null元素放在第一位:
import java.util.Comparator; import java.util.SortedSet; import java.util.TreeSet; public class Main { public static void main(String[] args) { // Sort the names based on their length, placing null first SortedSet<String> names = new TreeSet<>(Comparator.nullsFirst(Comparator .comparing(String::length))); names.add("XML"); names.add("CSS"); names.add("HTML"); names.add(null); // Adds a null // Print the names names.forEach(System.out::println); } }
上面的代码生成以下结果。
Java 导航集
Java集合教程 - Java导航集
导航集是一个有序集,允许您使用其子集在各种方式。
NavigableSet
表示Java集合中的可导航集合框架。 NavigableSet
接口继承了SortedSet接口和扩展 SortedSet
。
NavigableSet
界面可以以相反的顺序导航集合与SortedSet中定义的顺序相比。
headSet()
, tailSet()
和 subSet()
从 NavigableSet
接口接受布尔标志以将元素包括在子集边界的开始或结束处。
lower(),floor(),higher()和ceiling()从NavigableSet接口搜索基于搜索条件的元素。
lower()
方法返回小于指定元素的最大元素。
floor()
方法返回 NavigableSet
中小于或等于指定元素的最大元素。
higher()
方法返回最小元素大于指定元素的 NavigableSet
。
ceiling()
方法返回 NavigableSet
中大于或等于指定元素的最小元素。
pollFirst()
和 pollLast()
分别检索和删除 NavigableSet
的第一个和最后一个元素。 如果NavigableSet为空,它们返回null。
TreeSet
类是 NavigableSet
接口的实现类之一。我们可以使用 TreeSet
作为集合,排序集合和可导航集合。
TreeSet APIs
例子
import java.util.NavigableSet; import java.util.TreeSet; public class Main { public static void main(String[] args) { NavigableSet<Integer> ns = new TreeSet<>(); ns.add(0); ns.add(1); ns.add(2); ns.add(3); ns.add(4); ns.add(5); ns.add(6); // Get a reverse view of the navigable set NavigableSet<Integer> reverseNs = ns.descendingSet(); // Print the normal and reverse views System.out.println("Normal order: " + ns); System.out.println("Reverse order: " + reverseNs); NavigableSet<Integer> threeOrMore = ns.tailSet(3, true); System.out.println("3 or more: " + threeOrMore); System.out.println("lower(3): " + ns.lower(3)); System.out.println("floor(3): " + ns.floor(3)); System.out.println("higher(3): " + ns.higher(3)); System.out.println("ceiling(3): " + ns.ceiling(3)); System.out.println("pollFirst(): " + ns.pollFirst()); System.out.println("Navigable Set: " + ns); System.out.println("pollLast(): " + ns.pollLast()); System.out.println("Navigable Set: " + ns); System.out.println("pollFirst(): " + ns.pollFirst()); System.out.println("Navigable Set: " + ns); System.out.println("pollFirst(): " + ns.pollFirst()); System.out.println("Navigable Set: " + ns); System.out.println("pollFirst(): " + ns.pollFirst()); System.out.println("Navigable Set: " + ns); System.out.println("pollFirst(): " + ns.pollFirst()); System.out.println("pollLast(): " + ns.pollLast()); } }
上面的代码生成以下结果。
Java 列表
Java集合教程 - Java列表
列表是对象的有序集合,在 List
界面中定义。 List
接口表示集合框架中的列表。
列表可以具有重复的元素。并且我们可以在列表中存储多个空值。
List
接口继承了集合接口和它添加了一些方法来支持使用索引访问其元素。
我们可以在 List
或的末尾添加一个元素在由整数索引标识的任何位置。
列表中元素的索引是从零开始的。
我们可以使用以下方法使用索引添加,获取,删除和替换其元素。
its add(int index, E element), addAll(int index, Collection<? extends E> c), get(int index), remove(int index) set(int index, E element)
我们可以搜索元素的位置 List
使用indexOf(Object o)或 lastIndexOf(Object o)
方法。
indexOf()
方法搜索指定的对象从开始,它返回索引的对象的第一次出现。
lastIndexOf()
方法从列表的末尾搜索元素。两个方法都返回-1如果 List
不包含指定的对象。
subList(int fromIndex,int toIndex)
返回一个子列表的原始列表从索引fromIndex(包括)到索引toIndex(独占)。
ListIterator
从 List
接口可以返回在向前和向后方向上遍历其元素。
List APIs
ArrayList和LinkedList
以下是实现 List
接口的两个类:
- ArrayList
- LinkedList
ArrayList由数组备份。链接列表由链接列表备份。
如果我们频繁地访问列表的元素,那么 ArrayList
会表现得更好。访问ArrayList中的元素更快,因为它是数组后端。
从 ArrayList
添加或删除元素更慢除非从头到尾,因为 ArrayList
必须执行数组副本内部保持元素的顺序。
LinkedList
的性能优于 ArrayList
用于添加和从列表中间删除元素。但是,访问列表中的元素更慢,除非在列表的头部。
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Java"); list.add("Oracle"); list.add("CSS"); list.add("XML"); System.out.println("List: " + list); int count = list.size(); System.out.println("Size of List: " + count); // Print each element with its index for (int i = 0; i < count; i++) { String element = list.get(i); System.out.println("Index=" + i + ", Element=" + element); } List<String> subList = list.subList(1, 3); System.out.println(subList); // Remove "CSS" from the list list.remove("CSS"); // Same as list.remove(2); System.out.println(list); } }
上面的代码生成以下结果。
ListIterator
我们可以使用 ListIterator
接口以遍历列表。
ListIterator
接口继承了 Iterator
接口并且它增加了几个方法来从当前位置向后访问列表中的元素。
以下代码显示了如何从列表中获取列表迭代器:
ListIterator<String> fullIterator = list.listIterator();
为了得到在正向从索引5开始的列表迭代器,使用以下代码。
ListIterator<String> partialIterator = list.listIterator(5);
以下代码显示如何使用 ListIterator
。
import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class Main { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Oracle"); list.add("SQL"); list.add("CSS"); list.add("XML"); System.out.println("List: " + list); // Get the list iterator ListIterator<String> iterator = list.listIterator(); while (iterator.hasNext()) { int index = iterator.nextIndex(); String element = iterator.next(); System.out.println("Index=" + index + ", Element=" + element); } // Reuse the iterator to iterate from the end to the beginning while (iterator.hasPrevious()) { int index = iterator.previousIndex(); String element = iterator.previous(); System.out.println("Index=" + index + ", Element=" + element); } } }
上面的代码生成以下结果。
ListIterator
可以向前看或回头一个列表。
next()
方法向前移动一个索引,而 previous()
方法向后移动一个索引。
如果使用 next()
方法,后跟 previous()
方法,迭代器返回到相同的位置。