先来看看集合的继承关系图,如下图所示:
其中:
- 外框为虚线的表示接口,边框为实线的表示类;
- 箭头为虚线的表示实现了接口,箭头为实线的表示继承了类。
为了方便理解,我隐藏了一些与本文内容无关的信息,隐藏的这些内容会在后面的章节中进行详细地介绍。
从图中可以看出,集合的根节点是 Collection,而 Collection 下又提供了两大常用集合,分别是:
- List:使用最多的有序集合,提供方便的新增、修改、删除的操作;
- Set:集合不允许有重复的元素,在许多需要保证元素唯一性的场景中使用。
下面我们分别对集合类进行详细地介绍。
集合使用
1)Vector
Vector 是 Java 早期提供的线程安全的有序集合,如果不需要线程安全,不建议使用此集合,毕竟同步是有线程开销的。
使用示例代码:
Vector vector = new Vector();
vector.add("dog");
vector.add("cat");
vector.remove("cat");
System.out.println(vector);
程序执行结果:[dog]
2)ArrayList
ArrayList 是最常见的非线程安全的有序集合,因为内部是数组存储的,所以随机访问效率很高,但非尾部的插入和删除性能较低,如果在中间插入元素,之后的所有元素都要后移。ArrayList 的使用与 Vector 类似。
3)LinkedList
LinkedList 是使用双向链表数据结构实现的,因此增加和删除效率比较高,而随机访问效率较差。
LinkedList 除了包含以上两个类的操作方法之外,还新增了几个操作方法,如 offer() 、peek() 等,具体详情,请参考以下代码:
LinkedList linkedList = new LinkedList();
// 添加元素
linkedList.offer("bird");
linkedList.push("cat");
linkedList.push("dog");
// 获取第一个元素
System.out.println(linkedList.peek());
// 获取第一个元素,并删除此元素
System.out.println(linkedList.poll());
System.out.println(linkedList);
程序的执行结果:
dog
dog
[cat, bird]
4)HashSet
HashSet 是一个没有重复元素的集合。虽然它是 Set 集合的子类,实际却为 HashMap 的实例,相关源码如下:
public HashSet() {
map = new HashMap<>();
}
因此 HashSet 是无序集合,没有办法保证元素的顺序性。
HashSet 默认容量为 16,每次扩充 0.75 倍,相关源码如下:
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
HashSet 的使用与 Vector 类似。
5)TreeSet
TreeSet 集合实现了自动排序,也就是说 TreeSet 会把你插入数据进行自动排序。
示例代码如下:
TreeSet treeSet = new TreeSet();
treeSet.add("dog");
treeSet.add("camel");
treeSet.add("cat");
treeSet.add("ant");
System.out.println(treeSet);
程序执行结果:[ant, camel, cat, dog]
可以看出,TreeSet 的使用与 Vector 类似,只是实现了自动排序。
6)LinkedHashSet
LinkedHashSet 是按照元素的 hashCode 值来决定元素的存储位置,但同时又使用链表来维护元素的次序,这样使得它看起来像是按照插入顺序保存的。
LinkedHashSet 的使用与 Vector 类似。
集合与数组
集合和数组的转换可使用 toArray() 和 Arrays.asList() 来实现,请参考以下代码示例:
List<String> list = new ArrayList();
list.add("cat");
list.add("dog");
// 集合转数组
String[] arr = list.toArray(new String[list.size()]);
// 数组转集合
List<String> list2 = Arrays.asList(arr);
集合排序
在 Java 语言中排序提供了两种方式:Comparable 和 Comparator,它们的区别也是常见的面试题之一。下面我们彻底地来了解一下 Comparable 和 Comparator 的使用与区别。
1)Comparable
Comparable 位于 java.lang 包下,是一个排序接口,也就是说如果一个类实现了 Comparable 接口,就意味着该类有了排序功能。
Comparable 接口只包含了一个函数,定义如下:
package java.lang;
import java.util.*;
public interface Comparable {
public int compareTo(T o);
}
Comparable 使用示例,请参考以下代码:
class ComparableTest {
public static void main(String[] args) {
Dog[] dogs = new Dog[]{
new Dog("老旺财", 10),
new Dog("小旺财", 3),
new Dog("二旺财", 5),
};
// Comparable 排序
Arrays.sort(dogs);
for (Dog d : dogs) {
System.out.println(d.getName() + ":" + d.getAge());
}
}
}
class Dog implements Comparable<Dog> {
private String name;
private int age;
@Override
public int compareTo(Dog o) {
return age - o.age;
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
程序执行结果:
小旺财:3
二旺财:5
老旺财:10
如果 Dog 类未实现 Comparable 执行代码会报程序异常的信息,错误信息为:
Exception in thread "main" java.lang.ClassCastException: xxx cannot be cast to java.lang.Comparable
compareTo() 返回值有三种:
- e1.compareTo(e2) > 0 即 e1 > e2;
- e1.compareTo(e2) = 0 即 e1 = e2;
- e1.compareTo(e2) < 0 即 e1 < e2。
2)Comparator
Comparator 是一个外部比较器,位于 java.util 包下,之所以说 Comparator 是一个外部比较器,是因为它无需在比较类中实现 Comparator 接口,而是要新创建一个比较器类来进行比较和排序。
Comparator 接口包含的主要方法为 compare(),定义如下:
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator 使用示例,请参考以下代码:
class ComparatorTest {
public static void main(String[] args) {
Dog[] dogs = new Dog[]{
new Dog("老旺财", 10),
new Dog("小旺财", 3),
new Dog("二旺财", 5),
};
// Comparator 排序
Arrays.sort(dogs,new DogComparator());
for (Dog d : dogs) {
System.out.println(d.getName() + ":" + d.getAge());
}
}
}
class DogComparator implements Comparator<Dog> {
@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge() - o2.getAge();
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
程序执行结果:
小旺财:3
二旺财:5
老旺财:10
```先来看看集合的继承关系图,如下图所示:
![enter image description here](https://images.gitbook.cn/ae489970-ca62-11e9-bd50-998f3938aecb)
其中:
- 外框为虚线的表示接口,边框为实线的表示类;
- 箭头为虚线的表示实现了接口,箭头为实线的表示继承了类。
为了方便理解,我隐藏了一些与本文内容无关的信息,隐藏的这些内容会在后面的章节中进行详细地介绍。
从图中可以看出,集合的根节点是 Collection,而 Collection 下又提供了两大常用集合,分别是:
- List:使用最多的有序集合,提供方便的新增、修改、删除的操作;
- Set:集合不允许有重复的元素,在许多需要保证元素唯一性的场景中使用。
下面我们分别对集合类进行详细地介绍。
### 集合使用
#### 1)Vector
Vector 是 Java 早期提供的线程安全的有序集合,如果不需要线程安全,不建议使用此集合,毕竟同步是有线程开销的。
使用示例代码:
```java
Vector vector = new Vector();
vector.add("dog");
vector.add("cat");
vector.remove("cat");
System.out.println(vector);
程序执行结果:[dog]
2)ArrayList
ArrayList 是最常见的非线程安全的有序集合,因为内部是数组存储的,所以随机访问效率很高,但非尾部的插入和删除性能较低,如果在中间插入元素,之后的所有元素都要后移。ArrayList 的使用与 Vector 类似。
3)LinkedList
LinkedList 是使用双向链表数据结构实现的,因此增加和删除效率比较高,而随机访问效率较差。
LinkedList 除了包含以上两个类的操作方法之外,还新增了几个操作方法,如 offer() 、peek() 等,具体详情,请参考以下代码:
LinkedList linkedList = new LinkedList();
// 添加元素
linkedList.offer("bird");
linkedList.push("cat");
linkedList.push("dog");
// 获取第一个元素
System.out.println(linkedList.peek());
// 获取第一个元素,并删除此元素
System.out.println(linkedList.poll());
System.out.println(linkedList);
程序的执行结果:
dog
dog
[cat, bird]
4)HashSet
HashSet 是一个没有重复元素的集合。虽然它是 Set 集合的子类,实际却为 HashMap 的实例,相关源码如下:
public HashSet() {
map = new HashMap<>();
}
因此 HashSet 是无序集合,没有办法保证元素的顺序性。
HashSet 默认容量为 16,每次扩充 0.75 倍,相关源码如下:
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
HashSet 的使用与 Vector 类似。
5)TreeSet
TreeSet 集合实现了自动排序,也就是说 TreeSet 会把你插入数据进行自动排序。
示例代码如下:
TreeSet treeSet = new TreeSet();
treeSet.add("dog");
treeSet.add("camel");
treeSet.add("cat");
treeSet.add("ant");
System.out.println(treeSet);
程序执行结果:[ant, camel, cat, dog]
可以看出,TreeSet 的使用与 Vector 类似,只是实现了自动排序。
6)LinkedHashSet
LinkedHashSet 是按照元素的 hashCode 值来决定元素的存储位置,但同时又使用链表来维护元素的次序,这样使得它看起来像是按照插入顺序保存的。
LinkedHashSet 的使用与 Vector 类似。
集合与数组
集合和数组的转换可使用 toArray() 和 Arrays.asList() 来实现,请参考以下代码示例:
List<String> list = new ArrayList();
list.add("cat");
list.add("dog");
// 集合转数组
String[] arr = list.toArray(new String[list.size()]);
// 数组转集合
List<String> list2 = Arrays.asList(arr);
集合排序
在 Java 语言中排序提供了两种方式:Comparable 和 Comparator,它们的区别也是常见的面试题之一。下面我们彻底地来了解一下 Comparable 和 Comparator 的使用与区别。
1)Comparable
Comparable 位于 java.lang 包下,是一个排序接口,也就是说如果一个类实现了 Comparable 接口,就意味着该类有了排序功能。
Comparable 接口只包含了一个函数,定义如下:
package java.lang;
import java.util.*;
public interface Comparable {
public int compareTo(T o);
}
Comparable 使用示例,请参考以下代码:
class ComparableTest {
public static void main(String[] args) {
Dog[] dogs = new Dog[]{
new Dog("老旺财", 10),
new Dog("小旺财", 3),
new Dog("二旺财", 5),
};
// Comparable 排序
Arrays.sort(dogs);
for (Dog d : dogs) {
System.out.println(d.getName() + ":" + d.getAge());
}
}
}
class Dog implements Comparable<Dog> {
private String name;
private int age;
@Override
public int compareTo(Dog o) {
return age - o.age;
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
程序执行结果:
小旺财:3
二旺财:5
老旺财:10
如果 Dog 类未实现 Comparable 执行代码会报程序异常的信息,错误信息为:
Exception in thread "main" java.lang.ClassCastException: xxx cannot be cast to java.lang.Comparable
compareTo() 返回值有三种:
- e1.compareTo(e2) > 0 即 e1 > e2;
- e1.compareTo(e2) = 0 即 e1 = e2;
- e1.compareTo(e2) < 0 即 e1 < e2。
2)Comparator
Comparator 是一个外部比较器,位于 java.util 包下,之所以说 Comparator 是一个外部比较器,是因为它无需在比较类中实现 Comparator 接口,而是要新创建一个比较器类来进行比较和排序。
Comparator 接口包含的主要方法为 compare(),定义如下:
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator 使用示例,请参考以下代码:
class ComparatorTest {
public static void main(String[] args) {
Dog[] dogs = new Dog[]{
new Dog("老旺财", 10),
new Dog("小旺财", 3),
new Dog("二旺财", 5),
};
// Comparator 排序
Arrays.sort(dogs,new DogComparator());
for (Dog d : dogs) {
System.out.println(d.getName() + ":" + d.getAge());
}
}
}
class DogComparator implements Comparator<Dog> {
@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge() - o2.getAge();
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
程序执行结果:
小旺财:3
二旺财:5
老旺财:10