Java之集合(一)接口及抽象类

  转载请注明源出处:http://www.cnblogs.com/lighten/p/7278655.html

1.前言

  从本章开始介绍Java的集合类,这些类主要存在于java.util包下,该系列基于JDK8,类中所包含的JDK8的lambda表达式的相关内容不进行介绍,只关注集合本身的使用方法。

2.集合体系

  

  上图就是Java中的集合主要层次结构了,所有的集合都需要实现Iterable<T>接口,最上层的是通用的Collection接口定义,然后就是较为熟悉的三种结构:List、Queue和Set了。下面又根据不同的特性有了子类接口。右图就是相关的抽象实现了,所有具体的类都会对应继承这些抽象实现。Map不在本章讨论范围内,之后涉及到的时候会先讲解Map的实现。

3.接口定义和部分实现

3.1 Collection

  上图是JDK8中集合的接口定义,不关注JDK8的新特性,有如下接口:

  1.size():集合中元素的个数

  2.isEmpty():集合是否为空

  3.contain(Object):集合是否包含某个元素

  4.iterator():获取集合的迭代器

  5.toArray():返回集合中的元素数组

  6.toArray(T[]):将集合中的元素放入所给数组中,如果所给数组大于集合大小,补充null。如果数组小于集合大小,返回数组大小的集合元素(实际集合元素个数小于数组大小依旧补充null)。

  7.add(E):向集合中添加一个元素

  8.remove(Object):在集合中移除指定的元素

  9.containsAll(Collection<?>):集合是否包含指定集合的全部元素,有一个没有就返回false

  10.addAll(Collection<?>):将指定的集合中全部的元素添加到该集合中,返回值的含义并不是全部成功,而是集合的元素有所改变就是true.(做并集A∪B)

  11.removeAll(Collection<?>):将指定的集合中全部的元素都从该集合中移除,返回值的含义是移除操作造成该集合有所改变就是true。(A-A∩B)

  12.retainAll(Collection<?>):这个方法和removeAll相反,其含义是该集合只保留指定集合中的元素,移除其它元素,有改变就是true。(实际就是做交集,保留交集部分A∩B)

  上述方法就是每一个集合所必备的相关方法。AbstractCollection抽象类中,几乎实现了所有的接口方法,除了以下三个:size()、iterator()和add()方法,其它方法的实现都是基于这三个抽象方法的(add不是抽象方法,其直接抛出异常,需要子类覆盖,所以此处也视为抽象方法)。当然,实现的方法也许在子类中会被覆盖,不过也基本是实现完了可以实现的功能。

3.2 List

  List接口继承自Collection接口。除了Collection接口的方法外,还增加了以下与位置相关的方法:

  1.get(int):获取指定位置的元素

  2.set(int,E):替换指定位置的元素

  3.add(int,E):将元素插入指定位置

  4.remove(int):移除指定位置的元素

  5.indexOf(Object):返回指定元素第一次在list中出现的位置,没有就是-1

  6.lastIndexOf(Object):返回指定元素最后一次在list中出现的位置,没有就是-1

  7.listIterator():返回一个list类型的迭代器,比一般的迭代器提供了前移的方法

  8.listIterator(int):返回一个从指定位置开始的list的list类型迭代器。

  9.subList(int,int):返回一个子list,范围是[start,end)

  10.addAll(int, Collection):将一个集合中的元素从指定位置开始全部插入

  list接口的抽象类AbstractList也是对应的继承了AbstractCollection类,当然也需要实现list接口。对于AbstractCollection中实现的方法,AbstractList大体没有变化,除了add(E)和iterator()方法其有具体实现了。add(E)方法是通过add(int,E)方法实现的,其它与位置有关list接口定义的方法都没有实现,需要具体子类去覆盖实现。其它值得一提的方法有indexOf和lastIndexOf,clear()等方法,其实现都是通过listIterator()迭代器完成的,而不是一般的迭代器。而后重要的就是三个实现类了:Itr、ListItr和SubList。

  Itr这个普通的迭代器的内容很简单,就如上图。cursor是当前读取的元素下标,所以hasNext的判断就是其不等于list大小就是true。lastRet是记录上一个位置,原因是调用next的时候cursor会自增1,这个时候remove方法就得需要上一次的位置才能删除当前对象。expectedModCount是用来判断并发修改的,迭代器虽然允许边遍历边删除,但是并不允许并发遍历删除。了解了这个,其实现也就很好理解:

  ListItr的实现是继承了Itr类的,其就是增加了前移操作和set、add操作,操作也依旧容易,根据Itr的原理,只需要处理cursor和lastRet就可以了。

  set和add方法就是通过当前位置来调用list的相关方法完成。

  SubList是直接继承自AbstractList类的,其接受一个list,和起始,终止坐标。但是要注意的是,SubList持有的是传入的list的引用,对SubList进行修改操作都会影响原list的内容,其只是限定了一下操作边界而已,并不是真正意义上的一个新的拷贝list。并且多线程操作sublist是不允许的,按照代码来看,多线程操作不同的sublist,这些sublist持有同一个list也是不被允许的。

    @Test
	public void test() {
		List<Integer> list = new ArrayList<>();
		for(int i = 0; i < 5; i++) {
			list.add(i);
		}
		List<Integer> sub = list.subList(1, 3);
		sub.set(0, 100);
		System.out.println(Arrays.toString(list.toArray()));
	}

测试多线程的情况:

    @Test
	public void test2() throws InterruptedException {
		int threadNum = 3;
		int all = 10000;
		List<Integer> list = new ArrayList<>();
		for(int i = 0; i < all; i++) {
			list.add(i);
		}
		int per = all / threadNum;
		CountDownLatch latch = new CountDownLatch(threadNum);
		ExecutorService service = Executors.newCachedThreadPool();
		CyclicBarrier barrier = new CyclicBarrier(threadNum);
		for(int i = 0; i < threadNum; i++) {
			service.submit(new TestTask(list.subList(i*per, (i+1)*per), barrier, latch));
		}
		latch.await();
	}


class TestTask implements Runnable {
	
	private List<Integer> sub;
	private CyclicBarrier barrier;
	private CountDownLatch latch;
	
	public TestTask(List<Integer> sub, CyclicBarrier barrier, CountDownLatch latch) {
		this.sub = sub;
		this.barrier = barrier;
		this.latch = latch;
	}
	
	public TestTask(List<Integer> sub, CountDownLatch latch) {
		this.sub = sub;
		this.latch = latch;
	}

	@Override
	public void run() {
		try {
			if(barrier != null) {
				barrier.await();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
		try {
			System.out.println(Thread.currentThread().getName());
			for(int i = 0; i < sub.size(); i++) {
				sub.remove(i);
			}
			System.out.println(Thread.currentThread().getName()+" over!");
		} catch(Throwable e) {
			e.printStackTrace();
		} finally {
			latch.countDown();
		}
	}
	
}

  这个测试实际上并不规范,第一个实际运行的时候没有模拟出并发,看允许还是一个个线程执行的,第二个是sublist并不是抽象父类中的sublist,ArrayList重写了这个sublist。但还是可以参考一下。

3.3 Queue

  Queue队列结构也是继承了Collection接口,上图是其独自定义的一些方法。

  1.add(E):添加一个元素入队列,队列有容量限制时,超过容量抛出异常

  2.offer(E):添加一个元素入队列,在队列有容量限制的时候比add方法更好,不会抛出异常

  3.remove():检索并移除队列头,与poll不一样的地方在于如果队列为空,抛出异常

  4.poll():检索并移除队列头,队列为空返回NULL

  5.element():检索不移除,返回队列头,队列为空抛出异常

  6.peek():检索不移除,返回队列头,队列为空返回NULL

  AbstractQueue继承了AbstractCollection类,并实现Queue接口。其实现了add、remove和element方法,做法是借助未实现的offer、poll和peek方法,没有什么可说的地方。

 3.4 Set

  Set接口继承了Collection,并没有添加新的方法。其抽象实现类AbstractSet也没有做什么改变,AbstractCollection未实现的add,size,iterator均没有实现,只是重写了一下removeAll方法,减少了一下迭代次数而已。

原文地址:https://www.cnblogs.com/lighten/p/7278655.html