在实际开发中,经常需要将使用的对象存储于特定数据结构中。Java 提供了这样的容器:数组与集合,但是数组是定长的,操作数据过于繁琐,所以今天在这里介绍下集合。
Collection (接口)
集合:不定长,集合操作相对简单。Collection 是一个接口,定义了集合的相关操作方法,它有两个子接口:List 和 Set。
Collection c = new Collection();
List list = new List();
Set set = new Set();
以上三行都会报错,接口不能实例化
### List List 是可重复元素,且有序(与放入顺序一致)的集合。 >元素是否重复取决于元素自身的 equals 比较
实现 List 接口的类有两个: ArrayList 和 LinkedList。
Collection al = new ArrayList();//创建 ArrayList 集合,当然也可以用 List 声明
List ll = new LinkedList();//创建 LinkedList 集合
List 是有序可重复的:
al.add("1");
al.add("2");
al.add("3");
al.add("2");
al.add("3");
System.out.println(al);//输出 [1,2,3,2,3]
### Set Set 无序且不可重复的集合,实现 Set 接口的类也有两个:HashSet 和 TreeSet。 >因为不能重复,所以最多也只能包含一个 null。
Set hs = new HashSet();//创建 HashSet 集合
Set ts = new TreeSet();//创建 TreeSet 集合
hs.add("2");
hs.add("1");
hs.add("3");
hs.add("2");
hs.add("3");
System.out.println(hs);//输出 [3,2,1]
集合存储的元素数据类型可以不固定,但最好在使用时统一数据类型,方便后续操作
hs.add("1");//字符串类型
hs.add('2');//字符类型
hs.add(3);//int 类型
## Collection 的核心方法 ## ### add 方法 **add(E e)方法会将给定的元素 e 添加到集合中,返回 boolean 类型值,添加成功返回 true, 否则返回 false。** >一般并不需要使用它的返回值
List list = new ArrayList();
boolean isAdd = list.add("one");
System.out.println("添加元素 one 是否成功:" + isAdd);//true
list.add("two");
list.add("three");
list.add("four");
System.out.println("集合 list 为:" + list);//输出 [one, two, three, four]
### contains 方法 **contains(Object o)方法用于判断给定元素是否被包含在集合中,并返回 boolean 类型值,包含则返回 true,否则返回 false。(根据 equals 比较)**
boolean isContains = list.contains("three");
System.out.println("该集合是否包含 three:" + isContains);//true
isContains = list.contains("five");
System.out.println("该集合是否包含 five:" + isContains);//false
### remove 方法 **remove(Objct o)方法用于从集合中删除给定元素,并返回 boolean 类型值。删除成功则返回 true,否则返回 false。**
boolean isRemove = list.remove("two");
System.out.println("从集合中删除 two 元素是否成功:" + isRemove);//true
System.out.println("集合 list 为:" + list);//输出 [one, three, four]
isRemove = list.remove("five");
System.out.println("从集合中删除 five 元素是否成功:" + isRemove);//false
### size 方法 集合中的 **size() 方法**与数组中的 length 方法类似,**返回当前集合中的元素个数(int 类型)**。
System.out.println("集合 list 中元素的个数为:" + list.size());//输出 3
### clear 方法 clear() 方法可以清空集合中的所有元素。该方法没有返回值。
list.clear();
System.out.println("集合 list 为:" + list);//输出 []
### isEmpty 方法 **isEmpty() 方法用于判断当前集合是否为空(不包含任何元素),并返回 boolean 类型值,集合为空则返回 true,否则返回 false。**
if (list.isEmpty()) {
System.out.println("该集合不包含任何元素");
} else {
System.out.println("该集合包含元素");
}
list.add("five");
if (list.isEmpty()) {
System.out.println("该集合不包含任何元素");
} else {
System.out.println("该集合包含元素");
}
### addAll 方法 **addAll(Collection c) 方法需要传入一个集合,并将传入的集合中所有元素添加到当前元素集合,并返回 boolean 类型值,如果当前集合发生改变,则返回 true,否则返回 false。**
List arrayList = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
List linkedList = new LinkedList();
list.add(4);
list.add(5);
list.add(6);
boolean addAll = arrayList.addAll(linkedList);
System.out.println("arrayList 中是否添加了 linkedList 中的全部元素:" + addAll);
System.out.println("集合 arrayList 为:" + arrayList);//输出
### containsAll 方法 **containsAll(Collection c) 方法需要传入一个集合,判断当前集合是否包含传入集合中的所有元素,并返回 boolean 类型值,若包含则返回 true,否则返回 false。**
boolean isContainsAll = arrayList.containsAll(linkedList);
System.out.println("arrayList 中是否包含 linkedList 中的全部元素" + isContainsAll);//true
linkedList.add(7);
isContainsAll = arrayList.containsAll(linkedList);
System.out.println("arrayList 中是否包含 linkedList 中的全部元素" + isContainsAll);//false
集合与数组之间互换
collection 中定义了一个方法用于将集合转换为数组,所用集合都具备此功能
T[] toArray(T[] a)
Integer[] i = arrayList.toArray(new Integer[]{});
System.out.println("数组 i :" + Arrays.toString(i));
数据转集合:Arrays 类提供了一个静态方法 asList();
使用该方法可以讲一个数组转换成对应的集合。
List
该方法会将数组转换成集合(只能是 List 集合)
返回的集合元素由数组类型决定。
String[] arr = {1,2,3,4,5};
List<String> list = Arrays.asList(arr);
System.out.println(list);
返回的集合我们不能增删元素,否则抛出异常。因为操作集合都会影响到数组,而增删元素会导致数组的扩容与缩容。
//list.add("7");//
//解决方法所有几何都支持一个 Collection 作为参数的构造方法,该构造方法的作用是创建当前集合的同时包含给定集合所有的元素。
List<String> list2 = new ArrayList<String>(list);
System.out.println(list2);
对集合修改会影响数组对应的元素。
list.set(2,"three");
System.out.println(list);
System.out.println(Arrays.toString(arr));
更多方法请自行查看 API
## 泛型 ## Java 5.0 引入的新特性,定义了泛型,意味着所操作的数据类型被指定。 Java 泛型广泛应用在集合中,所有集合类都带有泛型参数,这样在创建集合的时候可以指定存放集合元素的类型,减少错误。
//创建集合 List 并指定元素为 String 类型
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add(3);//编译错误,只能放 String 类型的数据。
编译器会根据泛型来检查元素类型是否匹配。
## Iterator 迭代器(接口) ## 迭代器用于遍历集合,获取迭代器可以使用 Collection 提供的 iterator 方法。
Iterator<E> iterator();
Interator 提供一同一遍历集合元素的方式,
boolean hasNext():
用于判断当前集合是否有可迭代的元素。
E next():
用于去除当前迭代的元素。E 代表泛型。
迭代器遍历集合应遵循 问,取,删
规则,其中删除不是必须。
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(0);
list.add(2);
list.add(0);
list.add(3);
list.add(0);
list.add(4);
list.add(0);
list.add(5);
System.out.println(list);//输出 [1,0,2,0,3,0,4,0,5]
//获取迭代器
Interator<Integer> it = list.interator();
//进行遍历
while(it.hasNext()) {//问,是否含有元素
int i = it.next();//取,获取该元素
if (i == 0){ //非基本类型用 equals
//list.remove(i);//运行异常,使用迭代器遍历集合时,不能使用集合的 remove()方法,需要使用迭代器的删除方法。
it.remove(i);//删,删除该元素
}
System.out.println(i);
}
使用迭代器遍历集合时,不能使用集合的 remove()方法,需要使用迭代器的删除方法。
迭代器提供了 remove 方法,可以将调用迭代器的 next() 取出的元素删除。
### 增强型 for 循环 增强型 for 循环 是 Java 5.0 提供的一个新特性。该循环**不通用**与其他传统循环,一般只用于遍历集合或数组。 语法:
for (元素数据类型 变量名 : 集合或数组) {
//循环体
}
编译器在执行以上代码时,会将以上代码替换成迭代器去执行。(增强 for 循环本质上就是迭代器。)
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(new Integer(2));//自动拆装箱
list.add(3);
list.add(4);
for (int i = 5; i < = 10; i++) {
list.add(i);
}
System.out.println(list);//输出 [1,2,3,4,5,6,7,8,9,10]
//使用迭代器遍历
for (Integer i : list) {
System.out.prinln(i);
}
## List ## List 是一个**接口**,是 Collection 的子接口。可以将 List 理解成存放对象的数组,只不过元素个数不固定。 List 接口常见的两个实现类:ArrayList,LinkedList 分别用动态数组实现和双向链表实现。
- ArrayList:底层采用数组方式实现,可以通过下标来访问元素,并且下标从 0 开始,更适合用于随即访问(查询),但插入和删除性能稍慢。
- LinkedList:底层采用双向链表实现,更适合于插入和删除,随机访问速度较慢,在性能要求不是特别苛刻的情况下,可以忽略。
### List 特有的方法 List 除了继承 Collection 定义的方法之外,还定义了自己特有的方法。它**可以通过下标来操作元素**(Set 不可以)
**get(int index)**:返回集合中指定下标的元素。返回值为泛型。
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println("list 的第二个元素是:" + list.get(1));//输出 2
**set(int index, E element)**:将给定的元素存入到集合中指定位置,并将原位置的元素返回。返回值为泛型。
int x = list.set(2,5);//将第三个元素值设置为 5,并返回原先第三个元素的值,x = 3
System.out.println(list);//输出 [1,2,5,4]
**add(int index, E element)**:将给定的元素插入到指定位置,原位置及后续元素后移。**无返回值**。
list.add(1,7);//在第二个位置添加元素 7
System.out.println(list);//输出 [1,7,2,5,4]
**remove(index)**:删除集合中指定位置的元素,并返回该元素。返回值为泛型。
x = list.remove(2);//删除集合中第三个元素,并将该元素返回 x = 2
System.out.println(list);//输出 [1,7,5,4]
**subList(int startIndex, int endIndex)**:获取指定范围(含头不含尾)的子集合,返回该子集合。返回值为 List
List<Integer> subList = list.subList(1,3);
System.out.println(subList);//输出 [7,5]
子集的修改会影响父集
subList.set(1,subList.get(1)*10);
System.out.println(subList);//输出 [7,50]
System.out.println(subList);//输出 [1,7,50,4],父集合的元素也被修改了
### List 排序 Collection**s** 是集合的工具类,它提供了很多便于操作集合的方法,其中就有静态方法 sort() 方法进行排序,**仅能对 List 排序**。
**void sort(List
List<Integer> list = new ArrayList<Integer>();
Random r = new Random();
for (int i = 0; i < 10; i++) {
list.add(r.nextInt(100));
}
System.out.println("排序之前\n" + list);
Collections.sort(list);
System.out.println("排序之后\n" + list);
字符串也可以排序(默认按照 ascii 码进行排序)
List<String> strlist = new ArraysList<String>();
strlist.add("猴");
strlist.add("鼠");
strlist.add("牛");
strlist.add("马");
strlist.add("猪");
strlist.add("狗");
strlist.add("你");
System.out.println("排序之前\n" + strlist);
Collections.sort(strlist);
System.out.println("排序之后\n" + strlist);
#### Comparator 比较器 在实际开发中,往往需要自定义排序规则,需要自定义排序规则时,可以采用实现 Comparator **接口**来自定义排序规则。
//该 sort 方法支持一个重载方法,传入一个自定义比较器
Mycomparator com = new MyComparator();
Collections.sort(list, com);
//Collections.sort(list, new MyComparator());//这样也可以
//按照字符串长度排序
Collections.sort(strlist, new Comparator<String>() {//复习下匿名内部类...
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
);
自定义倒序排列比较器
Class MyComparator implements Comparator<Integer> {
/**
* 重写父类具体比较方法
* 若 o1 > o2 则返回值<0
* 若 o1 < o2 则返回值>0
* 若 o1 = o2 则返回至为 0
*/
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
## Set ## Set 也是一个接口,是 Collection 的子接口。不包含重复元素,存入和取出的顺序不一致,Set 最多只包含一个 null。 Set 接口有两个实现类:HashSet 和 TreeSet。
- HashSet:底层采用 Hash 表数据结构,HashSet 是通过元素的 HashCode 值相同,才会判断 equlas 是否为 true。若 HashCode 值不同,则不会调用 equals 进行判断。
- TreeSet:底层采用 二叉树算法,TreeSet 存储对象时,可以进行排序,但需要指定排序算法,依赖 complareTo 方法。
自定义的类需要重写 toString 和 equals 方法。
如果重写 equals 方法,一定要重写 [HashCode](/2017/03/19/HashCode 散列码/#散列函数) 方法。
equals 与 hashCode 的定义必须一致:如果 x.queals(y) 返回 true,那么 x.hashCode 就必须与 y.hashCode() 具有相同的值。
//自定义一个 Point 类
public class Point {
int x;
int y;
//无参构造器
public Point() {
}
//有参构造器
public Point(int x, int y) {
this.x = x;
this.y = y;
}
//重写 toString() 方法
public String toString() {
return "[" + x + "," + y + "]";
}
//重写 equals() 方法
public boolean equals(Object obj){
if(obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj instanceof Point){
Point p = (Point)obj;
return p.x == this.x && p.y == this.y;
}
ruturn false;
}
//重写 hashCode() 方法
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
}
测试类中使用:
Set<Point> set = new HashSet<Point>();
set.add(new Point(1,1));
set.add(new Point(2,7));
set.add(new Point(3,6));
set.add(new Point(1,1));
set.add(new Point(3,6));
System.out.println(set);