集合


在实际开发中,经常需要将使用的对象存储于特定数据结构中。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 Arrays.asList(数组);
该方法会将数组转换成集合(只能是 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)**方法对给定的集合元素进行自然排序(从小到大),无返回值。

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);

文章作者: CrazyBunQnQ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 CrazyBunQnQ !
 上一篇
Hash Code 散列码 Hash Code 散列码
散列码(hash code)散列码是由对象的实例域产生的一个整数。更准确的说,具有不同数据域的对象将产生不同的散列码。散列码是没有规律的。如果 x 和 y 是两个不同的对象,x.hashCode() 与 y.hashCode() 基本上不会
2017-03-18
下一篇 
日期类 日期类
在编程中,经常需要对日期和时间进行操作,这篇文章就介绍下常用的日期操作方法及其相关的类。
2017-03-16
  目录