集合
集合框架
1、Collection
java.util.Collection 接口
集合是用来保存一组元素的,不同的实现类,实现了不同的数据结构
Collection是所有集合的顶级接口,规定了所有集合都必须具备的功能
集合与数组一样,保存一组元素,但是操作元素的方法集合提供了
Collection下面有两个常见的子接口(分类)
2、List(可重复集合)
java.util.List:线性表,特点是可以存放重复元素,并且有序
3、Set(不可重复集合)
java.util.Set:不可以重复集合,大部分实现类是无序的
是否为重复元素是根据元素自身equals比较的结果判定的
集合持有对象的引用
集合中存储的都是引用类型元素
并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合
1、集合只能放引用类型元素,不能放基础类型
Collection c = new ArrayList();
c.add(1);
System.out.println(c);
若放的是基础类型,在jdk5之后或触发其自动拆装箱特性,将基本类型转换为对应的引用类型
2、集合只保存每个元素对象的引用(元素地址,非元素对象)
public class CollectionDemo2 {
public static void main(String[] args) {
Point p = new Point(1,2);
Collection c = new ArrayList();
//把p对象添加到集合中(添加的是p对象的地址)
c.add(p);
System.out.println("p:"+p);//(1,2)
System.out.println("c:"+c);//[(1,2)]
//修改p对象的值,发现集合中的元素也发生改变
p.setX(2);
System.out.println("p:"+p);//(2,2)
System.out.println("c:"+c);//[(2,2)]
}
}
面试考题
问输出结果(考点,内存的存储,方法区、栈与堆)
package collection;
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
int a = 1;
String s = "hellow";
Point p = new Point(1,2);
Collection c = new ArrayList();
c.add(p);
test(a,s,p,c);
System.out.println("a:"+a);//a:1
System.out.println("s:"+s);//s:hellow
System.out.println("p:"+p);//p:(2,2)
System.out.println("c:"+c);//c:[(2,6)]
//注意最后一个不是(5,6)
//因为在add后,p修改了x为2了,这时c中p指向的也发生改变了
}
public static void test(int a,String s,Point p,Collection c){
a++;
s = s+"world";
p.setX(2);
p = new Point(5,6);
c.clear();
c.add(p);
p.setX(a);
c = new ArrayList();
c.add(new Point(7,8));
}
}
集合中的常用方法
1、添加元素(add)
boolean add(E e)
向集合中添加元素,若成功添加则返回值为true
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
2、添加元素(集合之间求并集 addAll)
boolean addAll(Collection c)
向集合中添加另外一个集合的所有元素(set集合只会添加不重复的)
若有元素添加成功则返回值为true
注意:相当于求两集合的并集,如果是Set(不可重复)列表,还可以去重
public class CollectionDemo3 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c");
c1.add("c++");
System.out.println("c1:"+c1);
Collection c2 = new ArrayList();
c2.add("php");
c2.add("java");
c2.add("python");
c2.add("go");
System.out.println("c2:"+c2);
c1.addAll(c2);
System.out.println("c1:"+c1);
//c1:[java, c, c++, php, java, python, go]
}
}
3、查看集合长度(size)
int size() 返回当前集合的元素个数
相当于数组中的.length()方法
int size = c.size();
System.out.println("size:"+size);
4、判断集合是否为空集(isEmpty)
boolean isEmpty() 判断当前集合是否为空集
**注意:**这里的空集是,集合存在,集合不为null,集合里面没有存放东西,集合里面是空的
boolean isEmpty = c.isEmpty();
System.out.println(isEmpty);
5、清空集合(clear)
集合.clear(); 清空集合中的元素,不会删除集合
c.clear();
System.out.println("集合已经清空");
System.out.println(c.isEmpty());
6、判断给定元素是否被包含在集合中(contains)
boolean contains(Object o) 该方法会用于判断给定的元素是否被包含在集合中
若包含则返回true,否则返回false
**注意:**集合在判断元素是否被包含在集合中是根据每个元素的equals()方法进行比较后的结果
所以通常我们自定义的类要作为集合元素的话,有必要重写equals()保证contains()方法的合理结果
equals方法在不重写的情况下,Object定义的是使用"=="比较的
public class ContainsDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
//point为我们自定义的点类(包含x,y坐标)
c.add(new Point(1,2));
c.add(new Point(3,4));
c.add(new Point(5,6));
c.add(new Point(7,8));
//输出的集合元素是根据自定义类中的toString方法输出
System.out.println(c);
Point p = new Point(1,2);
//该判断根据equals判断(point类中我们重写过equals方法)
boolean contains = c.contains(p);
System.out.println("元素是否被包含:"+contains);
}
}
**注意:**集合输出的元素,根据元素所属类自定义的toString()方法来输出,默认输出引用地址
所以通常我们自定义的类要作为集合元素的话,也有必要重写toString()方法,来保证格式化输出
7、判断集合是否被真包含(containsAll)
boolean 当前集合.containsAll(给定集合);
判断当前集合是否包含给定集合中的所有元素
public class CollectionDemo3 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c");
c1.add("c++");
System.out.println("c1:"+c1);
Collection c3 = new ArrayList();
c3.add("c++");
c3.add("c");
System.out.println("c3:"+c3);
//判断c1是否包含c3中的所有元素
boolean containsAll = c1.containsAll(c3);
System.out.println("全包含:"+containsAll);
}
}
8、删除集合元素(remove)
boolean remove(Object o) 删除集合中的给定元素
**注意:**该方法也是根据equals判断找出,然后删除,若有重复元素,调用一次只会删除第一个
public class RemoveDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(3,4));
c.add(new Point(5,6));
c.add(new Point(7,8));
System.out.println(c);
Point p = new Point(1,2);
//remove也是依靠equals比较
//有重复元素,调用一次只会删除第一个
c.remove(p);
System.out.println(c);
}
}
9、删除交集元素(removeAll)
boolean 当前集合.removeAll(给定集合);
删除当前集合中与给定集合的共有元素,给定集合元素不发生变化
注意:只是删除调用方法的集合元素,删除的是两个集合共有的元素
public class CollectionDemo3 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c");
c1.add("c++");
System.out.println("c1:"+c1);
Collection c3 = new ArrayList();
c3.add("c++");
c3.add("c");
System.out.println("c3:"+c3);
//删除c1中c3的所有元素
c1.removeAll(c3);
System.out.println("c1:"+c1);
}
}
集合的遍历(迭代器)
1、遍历集合
集合提供了统一的遍历操作
无论哪种数据结构实现的集合都提供了该遍历方式:迭代器模式
Iterator iterator()
Collection提供的iterator方法可以获取一个用于遍历当前集合的迭代器
java.util.Iterator接口
该接口是迭代器接口,规定了遍历集合的相关操作
所有集合都有一个用于遍历自身的迭代器实现类
我们无需关注它们的类名,以多态的角度用该接口看待并调用相关遍历方法即可
使用迭代器遍历集合的统一方式遵循为:
问->取->删 (其中删除元素不是必须操作)
2、实现步骤:
- 先创建迭代器对象(Iterator it),接收集合调用iterator方法获取的一个用于遍历当前集合的迭代器
Iterator it = c.iterator();
该步可以利用向上造型,无需关心迭代器的类名,使用Iterator接口接收即可 - 问:it.hasNext(),迭代器通过hasNext方法查看有没有下一个元素,若有返回true
- 取:it.next(),迭代器通过next方法,取出该元素,返回值为Object类(若确定返回值的具体类型,可以采取相应的强转)
**注意:**问与取要交替调用,问一次取一次,这个取并不会对原集合造成影响
public class IteratorDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println("c:"+c);
//创建迭代器引用,接收返回的迭代器对象
Iterator it = c.iterator();
//问:迭代器通过hasNext方法查看有没有下一个元素,若有返回true
while(it.hasNext()){
//取:迭代器通过next方法,取出下一个元素,返回值为Object类
//这里强转为了String类
String str = (String)it.next();
System.out.println(str);
}
}
}
3、通过迭代器删除元素
迭代器提供了remove方法
该方法不需要传入参数,删除的就是本次遍历通过next获取的元素
删除元素也遵循问取删原则
**注意:**在迭代器遍历元素时,是不允许通过集合操作,增删元素的,否则会抛异常
java.util.ConcurrentModificationException
public class IteratorDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one");
c.add("#");
c.add("two");
c.add("#");
c.add("three");
c.add("#");
c.add("four");
c.add("#");
c.add("five");
System.out.println("c:"+c);
Iterator it = c.iterator();
//问:迭代器通过hasNext方法查看有没有下一个元素,若有返回true
while(it.hasNext()){
//取:迭代器通过next方法,取出下一个元素,返回值为Object类
String str = (String)it.next();
System.out.println(str);
//删除满足条件元素
if("#".equals(str)){
//不能通过集合方法删除
//c.remove(str);
//通过迭代器对象删除,删除next获取的元素
it.remove();
}
}
System.out.println("c:"+c);
}
}
4、增强for循环(for each)
JDK5之后推出了一个特性:增强for循环
也称为新循环,采用了迭代器的原理,它不取代传统for循环的工作
仅用来遍历集合或数组使用,遍历其中所有元素,而不需要使用下标值
语法格式:
for(类型 接收变量名 : 数组或集合){}
**注意:**新循环是编译器认可的,而不是虚拟机认可的,
编译器在编译源代码时会将新循环改成相应的格式,
遍历数组改为传统的for循环遍历,遍历集合改为迭代器
所以使用新循环遍历集合时,也不能操作集合增删集合元素
public class NewForDemo {
public static void main(String[] args) {
//使用新循环遍历数组
String[] array = {"one","two","three","four","five"};
for(String s : array){
System.out.println(s);
}
//使用新循环遍历集合
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
for(Object o : c){
String s = (String)o;
System.out.println(s);
}
}
}
泛型
泛型是JDK5之后推出的一个特性,又称为参数化类型
允许我们在使用一个类时指定他的属性、方法的参数和返回值类型,使得我们用起来更灵活
泛型的原型是Object,不指定时就使用它
泛型在集合中广泛使用,用于规定集合中的元素类型(集合中的泛型用来约束元素类型)
设计出来的初衷是,把变量类型的定义交给使用者,解决java作为强类型语言,变量命名时必须声明类型的问题
但其实泛型只是编译器支持,在运行时都是当做Object来看,底层实现用的是Object,我们可以通过反射破坏泛型(反射篇会提到)
public class CollectionDemo4 {
public static void main(String[] args) {
//<>里传入指定的类型,指定后,该集合只能存放该类型元素
Collection<String> c = new ArrayList<String>();
//JDK7以后,后面的<>指定泛型部分可以省略类型,编译器会自动和前面保持一致
//List<String> list = new ArrayList<>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
//迭代器也需要传参为集合指定的类型
Iterator<String> it = c.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
//可以使用指定的类型来接收迭代器对象
for(String s:c){
System.out.println(s);
}
}
}
线性表(List集合)
1、java.util.List 线性表
List集合是一个可以重复的集合,并且有序
特点是提供了一组通过下标操作元素的方法
2、常用实现类(ArrayList与LinkedList)
- java.util.ArrayList 数组
一段连续的存储空间,内部使用数组实现,查询性能更好,但是增删元素性能差 - java.util.LinkedList 链表
各元素独立的存储,通过节点来互相连接
内部使用链表实现,增删元素性能好,尤其首尾增删元素性能最佳,但是查询性能差
如果对性能没有特别苛刻的要求时,通常使用ArrayList即可
3、List常用方法(List独有)
- E get(int index) 获取给定下标处对应的元素(E指相应泛型)
- E set(int index,E e) 将给定元素设置到指定位置上(替换指定位置元素)
有返回值,返回值为原位置对应的元素,所以set是替换元素操作
- void add(int index,E e) 将给定元素插入到指定位置(集合重载的方法)
- E remove(int index) 删除并返回给定位置元素(集合重载的方法)
- List subList(int start,int end) 获取当前集合中指定范围的子集(注意含头不含尾)
**注意:**截取子集不会对原集合造成影响,但是截取出来的子集还是属于原集合,
对截取出来的子集修改,会影响原集合,修改子集元素就是修改原集合对应元素
public class ListDemo1 {
public static void main(String[] args) {
//JDK7以后,后面的<>指定泛型部分可以省略类型,编译器会自动和前面保持一致
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
//1、
//通过get方法获取第一个元素,用相应泛型接收
String s1 = list.get(0);
System.out.println("list中第一个元素"+s1);
/*
* 普通for循环遍历List集合(不用迭代器)
* 因为List集合可以通过下标获取
*/
for(int i=0;i<list.size();i++){
String str = list.get(i);
System.out.println(str);
}
//2、
//将第二个元素替换为2
//list.set(1, "2");
//可以用相应泛型接收替换下来的值
String s2 = list.set(1, "2");
System.out.println(list);
System.out.println("替换下的元素为:"+s2);
/*
* 在不创建新集合的情况下
* 将list集合元素颠倒位置
*/
//获取前后的值互换,只用走一半的循环
for(int i=0;i<list.size()/2;i++){
//将前面的数保存下来
String start = list.get(i);
//将前面的数替换掉后面对应位置的数,再将替换下来的保存
String end = list.set(list.size()-1-i, start);
//将替换下来的保存到开始的位置
list.set(i, end);
//缩写成一句话为:
//list.set(i, list.set(list.size()-1-i, list.get(i)));
}
System.out.println(list);
//3、
//在下标为3的位置添加元素"two"
list.add(3,"two");
System.out.println(list);
//4、
//删除下标为4的元素
//list.remove(4);
//用相应泛型接收删除的值
String s4 = list.remove(4);
System.out.println(s4);
System.out.println(list);
//5、
//获取下标3-5的集合元素子集(含下标元素3、4,因为含头不含尾)
List<String> sublist = list.subList(3, 5);
System.out.println(sublist);
}
}
练习:
public class ListDemo2 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//对列表赋值
for(int i=0;i<10;i++){
list.add(i,i);
}
System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//截取列表子集内容为3-7(要截取3-8,含头不含尾)
List<Integer> sublist = list.subList(3, 8);
System.out.println(sublist);//[3, 4, 5, 6, 7]
//不会对原集合造成影响,但是对该截取出来的子集修改,会影响原集合
System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//将子集元素扩大10倍
for(int i=0;i<sublist.size();i++){
sublist.set(i, sublist.get(i)*10);
}
System.out.println(sublist);//[30, 40, 50, 60, 70]
//注意这时会对原集合造成影响,原集合同样发生改变
//修改子集元素就是修改原集合对应元素
System.out.println(list);//[0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
//利用子集清空原集合中2-8之间的元素
list.subList(3, 9).clear();
System.out.println(list);//[0, 1, 2, 9]
}
}
集合和数组的互转
1、集合转数组的方法(toArray)
T[] toArray(T[] a)
Collection 中定义了一个方法toArray,可以将当前集合转换为数组
用自己指定的泛型数组,接收转换后的数组,如果不指定,默认为Object,需要用Object数组接收
**注意:**toArray(new String[c.size]) 小阔号里面传入我们自己指定的类型数组对象,
这个数组对象只作为模范,能用(大小够用)就用,如果不能用,编译器会自己按照这个模范创建一个字节能用的数组对象
代码示例:
package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionToArrayDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
//toArray默认转换为Object类数组
Object[] array = c.toArray();
//自己传入一个数组对象的模范,按照这个模范生成数组
String[] strArray = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(strArray));
//注意:这个数组模范能用则用,不能用会按照这个模范自己创建一个
String[] strArray2 = c.toArray(new String[0]);
System.out.println(Arrays.toString(strArray2));
}
}
2、数组转集合(asList)
List<T> Arrays.asList(数组) 数组转为List集合
数组的工具类提供了一个静态方法asList,可以将给定的数组转换为一个List集合
**注意:**只能转为List集合,因为数组不能重复
这个数组转换的集合其实是数组的映射,对这个集合元素的操作就是对相应数组元素的操作
所以,由于数组定长,因此改变由数组转换集合的元素个数的操作(增、删)都是不支持的
会抛出异常 java.lang.UnsupportedOperationException
为了解决这个问题,我们可以创建一个真正的新集合,将数组转换集合的元素,全部添加到新集合中
所有集合都支持一个参数为Collection类型的构造方法
作用是创建该集合的同时包含给定集合中的所有元素
public class ArrayToListDemo {
public static void main(String[] args) {
String[] array = {"one","two","three","four","five"};
//转换为相应的集合
List<String> list = Arrays.asList(array);
System.out.println(list);
//注意:对这个集合的元素操作,就是对原数组相应元素的操作
list.set(1, "2");
System.out.println(list);
//原数组也发生改变
System.out.println(Arrays.toString(array));
//由于这个原因,数组定长
//不能对这个数组转换的集合进行增删操作
//list.add("six");
//为了解决这个问题,我们可以创建新集合接收数组转换集合的元素
List<String> list1 = new ArrayList<>();
list1.addAll(list);
System.out.println(list1);
//使用集合的重载构造方法,传入一个Collection类型的参数
//创建该集合的同时包含给定集合中的所有元素
List<String> list2 = new ArrayList<>(list);
System.out.println(list2);
}
}
实现一步操作(将数组转换为真正的集合)
public class ArrayToListDemo {
public static void main(String[] args) {
String[] array = {"one","two","three","four","five"};
List<String> list = new ArrayList<>(Arrays.asList(array));
System.out.println(list);
}
}