Java进阶知识——集合

Java 集合

集合是什么

Java 集合类存放于java.util 包中,是一个用于存放对象的容器

  1. 集合中只能存放对象,如果存入一个简单类型,实际上进行了自动转换【装箱】
  2. 集合中存放的是多个对象的引用,对象本身还是存放在堆内存
  3. 集合中可以存放不同类型、不限数量的数据

集合接口:
规范所能够访问的方法
集合抽象类:
提供公共方法的实现
集合具体实现类:
提供独有的方法实现
集合工具类:
针对集合的通用算法的封装

Collection 接口

一般来说Collection 接口是集合框架的顶级接口,但是事实上不是

public interface Collection<E> extends Iterable<E>

特征: 集合中的数据无序,允许重复
Hello Collection 接口

// Collection collection=new ArrayList();
Collection collection=new HashSet();
for(int i=0;i<50;i++)
	collection.add(i%10); //集合中并不能直接存储简单类型数据,这里存放时会自动装箱
System.out.println(collection.size()); //获取存放的元素个数
//Collection 接口中并没有提供按照序号遍历元素的方法
for(Object tmp:collection)
	System.out.println(tmp);

Collection 接口定义的方法预览

public interface Collection<E> extends Iterable<E> {
	//获取集合中的元素个数
	int size();
	//判断集合中的元素个数是否为0,不能使用这个方法判断null
	boolean isEmpty();  
	//判断集合中是否包含指定元素o,有true 没有false。不能判断集合中是否包含null 元素
	boolean contains(Object o);  
	//Iterable 接口中定义的方法,获取迭代器对象用于遍历集合中的所有元素
	Iterator<E> iterator();  
	//将集合转换为Object 类型的数组
	Object[] toArray();  
	//使用泛型将集合转换为特定类型的数组
	<T> T[] toArray(T[] a);  
	//向集合中添加元素e
	boolean add(E e);   
	//从集合中删除指定的元素o,如果删除成功true 否则false 
	boolean remove(Object o); 
}

操作一组元素的方法

//通过lambda 表达式元素需要删除的元素
default boolean removeIf(Predicate<? super E> filter) { 
	Objects.requireNonNull(filter);
	boolean removed = false;
	final Iterator<E> each = iterator();
	while (each.hasNext()) {
		if (filter.test(each.next())) {
			each.remove();
			removed = true;
		}
	}
	return removed;
}
default Stream<E> stream() { //支持流式编程
	return StreamSupport.stream(spliterator(), false);
}

Iterable 可迭代的接口

Iterator 迭代器:走访器,可以理解为集合中元素的指针

public interface Iterable<T> {
	//通过iterator 方法获取迭代器
	Iterator<T> iterator(); 
	//支持使用Lambda 表达式遍历每个元素
	default void forEach(Consumer<? super T> action) { 
		Objects.requireNonNull(action);
		for (T t : this) {
			action.accept(t);
		}
	}
}

之所以所有的集合对象都可以使用foreach 接口,例如for(Object tmp:collection),正是因为Iterable 接口中提供了默认实现,如果具体实现类中不使用default 实现,则必须进行覆盖定义Iterator 迭代器

它是Java 集合的顶层接口(不包括map 系列的集合,Map 接口是map 系列集合的顶层接口)

public interface Iterator<E> {
	//判断容器内是否还有可供访问的元素
	boolean hasNext(); 
	//返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型
	E next();
}

对于大多数实现了Iterable 接口的集合可以多次调用forEach,并将通过元素进行多次传递

list.forEach((obj)->{System.out.println(obj);});
list.forEach(System.out::println);

forEachRemaining 方法获得对应集合的迭代器Iterator,然后可以开始迭代,next()直到达到某个条件,然后使用forEachRemaining()操作该Iterator 上的其余部分

Iterator it=list.iterator();
it.forEachRemaining((obj)->{
	System.out.println(obj);
});

foreach 写法(java: for-each 不适用于表达式类型)
所谓的foreach 结构是Iterator 迭代访问的简化写法

public class Test4 {
	public static void main(String[] args) {
		Person p=new Person();
		for(Object obj:p){
			System.out .println(obj);
		}
	}
}
class Person implements Iterable{
	@Override
	public Iterator iterator() {
		return new Iterator() {
			@Override
			public boolean hasNext() {
				return false;
			}
			@Override
			public Object next() {
				return null;
			}
		}; //实际上foreach 结构是通过Iterator 实现遍历集合的
	}
	@Override
	public void forEach(Consumer action) {
		Iterable.super.forEach(action);
	}
	@Override
	public Spliterator spliterator() {
		return Iterable.super.spliterator();
	}
}
Iterator 使用问题

Iterator 的安全失败是基于对底层集合做拷贝,因此它不受源集合上修改的影响。java.util 包下面的所有的集合类都是快速失败fail-fast 的,而java.util.concurrent 包下面的所有的类都是安全失败fail-safe 的。快速失败fail-fast 的迭代器会抛出ConcurrentModificationException 异常,而安全失败fail-safe 的迭代器永远不会抛出这样的异常。

  • fast-fail 事件产生的条件:
    当多个线程对Collection 进行操作时,若其中某一个线程通过iterator 去遍历集合时,
    该集合的内容被其他线程所改变;则会抛出ConcurrentModificationException 异常。
  • fast-fail 解决办法:
    通过java.util.concurrent 集合包下的相应类去处理,则不会产生fast-fail 事件。
Collection co=new ArrayList();
for(int i=0;i<20;i++)co.add(i);
new Thread(()->{
	Iterator it=co.iterator();
while(it.hasNext()){
	System.out.println(it.next());
	try {
		Thread.currentThread().sleep(100);
	} catch (InterruptedException e) {
		e.printStackTrace();
		}
	}
}).start();
Thread.currentThread().sleep(110);
co.add(100L);
  • fail-safe 安全失败:
    不抛出异常,不会中断程序的执行
Collection co = new CopyOnWriteArrayList();
for(int i=0;i<20;i++) co.add(i);
new Thread(()->{
	Iterator it=co.iterator();
	while(it.hasNext()){
		System.out.println(it.next());
		Thread.currentThread().sleep(100);
	}
}).start();;
Thread.currentThread().sleep(110);
co.add(100L);

Collection 接口方法

Collection 接口用于表示无序、允许重复的集合
构建Collection 对象
使用Collection 接口定义变量,使用new 创建具体的实现类

Collection co=new ArrayList(); 

由于使用接口定义变量,所以可以new 任何Collection 接口的实现类,例如HashSet、Vector、LinkedList 等【多态性】

//获取集合中的存储元素个数。size 表示实际存储的元素个数
int size(); 
//向集合中添加元素e
boolean add(E e); 
//判断集合中的元素个数是否为0,不能使用这个方法判断引用对象是否为null
boolean isEmpty(); 

注意:只判断集合中是否没有元素,并不会判断集合是否为null,如果需要判断集合变量是否为null,需要自行编程实现,例如

Objects.nonNull(co)或者co!=null

判断集合中是否包含指定元素o,有true 没有false。不能判断集合中是否包含null 元素。如果编程实现添加add(null)则可以判断正确,默认添加的null 并不能进行判断

boolean contains(Object o); 
//Iterable 接口中定义的方法,获取迭代器对象用于遍历集合中的所有元素
Iterator<E> iterator(); 

遍历Collection 集合中元素的写法

public class Test3 {
public static void main(String[] args) {
	Collection co=new ArrayList();
	ThreadLocalRandom r=ThreadLocalRandom.current();
	for(int i=0;i<10;i++)
		co.add(r.nextInt(100)); 
  1. 可以使用Iterable 接口提供的获取Iterator 对象进行遍历访问
	Iterator it=co.iterator(); //可以将迭代器理解为一个指向集合元素的指针
	while(it.hasNext()){ // 判断当前指针所指向的位置后续是否有元素,有true 没有false
		Object ele = it.next();//位置指针后移,并获取位置指针所指向的元素
		System.out.println(ele);
		ele=33L; // 通过迭代器直接修改集合元素无效,除非是原地修改
		it.remove();//删除当前位置指针所指向的元素
	}
  1. 使用Iterator 的forEachRemaining 方法遍历集合中的所有元素
	it=co.iterator();
	it.forEachRemaining((Object obj) ->{
		System.out.println(obj);
		obj=999; // 通过迭代器直接修改集合元素无效,除非是原地修改
	});
  1. 使用Collection 接口的父接口Iterable 中的方法forEach
	co.forEach((Object obj)-> {
		System.out.println(obj);
		obj=666; // 直接修改集合元素无效,除非是原地修改
	});
  1. foreach 结构
	for(Object tmp:co){
		System.out.println(tmp);
		tmp=888; // 直接修改集合元素无效,除非是原地修改
		}
	}
}

List 接口

List 接口是Collection 接口的子接口,用于定义有序、允许重复的集合
特征:数据有序【索引编号】允许重复。

  • 在Collection 接口的基础上添加了每个元素的索引编号,编号从0 开始到size-1 结束
 public interfaceList<E> extends Collection<E> 
  • 继承Collection 接口,所以Collection接口中的所有方法List都有

新增的方法: 都是和位置有关的方法

//向指定下标位置上添加元素,原始位置上的元素后移
void add(int index, E element); 
//删除指定位置上的元素,并且返回被删除的元素
E remove(int index);
//覆盖修改指定位置上的元素
E set(int index, E element);
//获取指定位置上的元素,index 取值返回[0,list.size()),超出范围则越界异常
E get(int index); 
//从前向后查找第一个指定的元素,依赖于对象中的equals 进行比较,如果不存在则返回-1
int indexOf(Object o);
//从后向前查找第一个指定的元素,依赖于对象中的equals 进行比较,如果不存在则返回-1
int lastIndexOf(Object o);

List 接口方法的具体使用

List 接口用于表示有序、允许重复的集合构建List 对象
使用List 接口定义变量,使用new 创建具体的实现类
由于使用接口定义变量,所以可以new 任何List 接口的实现类,例如ArrayList、Vector、LinkedList 等【多态性】

List co=new ArrayList(); 

特殊的迭代器ListIterator

Collection 接口的迭代器为Iterator 类型,只能单向迭代

ListIterator<E> listIterator();
public interface ListIterator<E> extends Iterator<E> {
	//判断位置指针之前是否有元素,有true 没有false
	boolean hasPrevious(); 
	//获取前一个元素
	E previous();
	//获取下一个元素的下标序号值
	int nextIndex(); 
	//获取前一个元素的下表序号值
	int previousIndex();
	//修改当前位置指针指向的元素,新值e 替换原始数据
	void set(E e);
	//在当前位置上添加一个新元素,原始位置上的数据后移
	void add(E e); 
}

Iterator 接口中的修改方法只有remove,但是ListIterator 接口中有修改set、新增add 和删除remove

数组和集合的比较

针对Java 中的数组定长,Java 提出了集合框架,实现了一种变长存储数据的容器———集合
数组不是面向对象的,存在明显的缺陷,集合弥补了数组的缺点,比数组更灵活更实用,而且不同的集合框架。
类可适用不同场合

  1. 数组能存放基本数据类型和对象,而集合类存放的都是对象的引用,而非对象本身
  2. 数组容量固定无法动态改变,集合类容量动态改变
  3. 数组无法判断其中实际存有多少元素,length 只告诉了数组的容量,而集合的size()可以确切知道元素的个数
  4. 集合有多种实现方式和不同适用场合,不像数组仅采用顺序表方式
  5. 集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性即可实现各种复杂操作,大大提高了软件的开发效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值