第8章:集合

8.1 集合概述

  1. 为保存数量不确定的数据,以及保存具有映射关系的数据,java提供了集合类
  2. 继承关系
    在这里插入图片描述
  3. Set、List、Queue、Map
    1. Set:不能重复,有序或无序
    2. List:可以重复,有序
    3. Queue:可以重复,有序,模拟队列
    4. Map:key放在一起为Set集合,不能重复,有序或无序

8.2 Collection与Iterator接口

8.2.1 Collection方法
//一定注意,将元素插入集合失败不会报错,而是返回false
boolean add(Object o):集合中添加元素
boolean addAll(Collection c):集合中添加集合
void clear():清空集合
boolean contains(Object o):集合中是否包含元素o
boolean containsAll(Collection c):结合中是否包含c集合中所有元素
boolean isEmpty():集合长度是否为0
Iterator iterator():返回Iterator对象来遍历集合中的元素
boolean remove(Object o):删除元素o
boolean removeAll(collection c):删除c集合中所包含的元素
boolean retainAll(collection c):与c集合取交集
int size():返回集合中元素个数
Object[] toArray():集合转数组
8.2.2 遍历集合中元素
  1. 使用Lambda表达式遍历集合中元素
import java.util.Collection;
import java.util.HashSet;

public class CollectionEach {
	public static void main(String[] args) {
		Collection books = new HashSet();
		books.add("book 1");
		books.add("book 2");
		books.add("book 3");
		//Collection继承了Iterable接口,Iterable在Java8中新增了forEach接口,可以遍历集合中的元素
		//forEach方法表示,对于集合中每个元素c,都做执行System.out.println("迭代集合中元素:" + c)
		books.forEach(c -> System.out.println("迭代集合中元素:" + c));
	}
}

  1. Iterator遍历集合中元素
package mytest.javaBase;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorTest {
	public static void main(String[] args) {
		List<String> books = new ArrayList<String>();
		books.add("1");
		books.add("2");
		books.add("java疯狂讲义");
		//Iterator对象必须依附于Collection对象
		Iterator<String> it = books.iterator();
		//hasNext:只要还没遍历完集合,就会返回true
		while(it.hasNext()){
		    //next:返回集合中的下一个元素
			String book = (String)it.next();
			if(book.equals("java疯狂讲义")){
			    //删除集合中最近一次next方法返回的元素
			    //Iterator迭代访问Collection中对象时,集合中元素只能通过Iterator的remove方法改变,不可用Collection对象的remove方法改变,否则会引发java.util.ConcurrentModificationException
			    //books.remove();
				it.remove();
			}
		}
		System.out.println(books);
	}
}

  1. Lambda表达式遍历Iterator
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class IteratorEach {
	public static void main(String[] args) {
		Collection books = new HashSet();
		books.add("book 1");
		books.add("book 2");
		books.add("book 3");
		Iterator it =  books.iterator();
		//Collection继承了Iterable接口,Iterable在Java8中新增了forEach接口,可以遍历集合中的元素
		//forEach方法表示,对于集合中每个元素c,都做执行System.out.println("迭代集合中元素:" + c)
		it.forEachRemaining(c -> System.out.println("迭代集合中元素:" + c));
	}
}

  1. foreach循环遍历集合中元素:
    1. 与Iterator循环相同,不可以用Collection对象的remove方法对集合进行改变
    2. 由于foreach中没有Iterator的remove方法,所以foreach循环只能遍历集合中元素,无法修改集合中元素
8.2.3 使用Java8新增的Predicate操作集合
  1. Predicate翻译过来叫做谓语(是、不是),或叫断言,实际上就是一个函数式接口,该函数式接口包含一个abstract的test方法,返回值为boolean类型
Collection<String> books = new HashSet<String>();
books.add("book 112121212");
books.add("book 2");
books.add("book 3");
//Collection中的removeIf方法,传入参数就是一个Predicate类型,集合会将元素传给Predicate对象的test方法,返回值为true时,该元素被删除
books.removeIf(c->c.length()<10);
System.out.println(books);
  1. 自定义一个类似于removeIf这种,需要传入Predicate的方法calAll
import java.util.Collection;
import java.util.HashSet;
import java.util.function.Predicate;

public class CollectionEach {
	public static void main(String[] args) {
		Collection<String> books = new HashSet<String>();
		books.add("book 112121212");
		books.add("book 2");
		books.add("book 3");
		//返回集合books中,元素包含book的个数
		System.out.println(calAll(books, book -> ((String) book).contains("book")));
	}

	private static int calAll(Collection books, Predicate p) {
		int total = 0;
		for (Object book : books) {
			if (p.test(book)) {
				total++;
			}
		}
		return total;
	}
}

8.2.4 Java8新增的Stream操作集合
8.2.4.1 Stream简介
  1. Stream:
    1. 代表数据元素有限/无限的顺序,它不是数据结构,不会保存数据,Stream pipeline表示数据元素的一个多级的运算
    2. 是流式的:即所有pipeline的调用可以链接成一个表达式
  2. Stream pipeline:
    1. 表示数据元素的一个多级的运算
    2. 包含一个源Stream,0个/多个中间操作,和一个终止操作
    3. 是lazy的,即调用终止操作时,才开始计算,对于终止操作不需要的数据元素,永远不会计算。
    4. 可以并行,但通常不建议
  3. Stream如同一个高级版本的 迭代器(Iterator),其末端方法是单向的,且不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返
  4. 不会修改原来的数据源,它会将操作后的数据保存到新对象中
8.2.4.2 创建Stream对象
  1. Collection.stream/parallelStream():集合转为流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
  1. Arrays.stream():数组转为流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
  1. Stream.of/iterate/generate():流的静态方法
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
 
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
 
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
  1. BufferedReader.lines():将IO流,每一行作为一个元素,转为流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
  1. Pattern.splitAsStream(final CharSequence input):将字符序列input以Pattern规定的方案,转为流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
8.2.4.3 Stream中方法分类
  1. 中间操作(intermediate):中间方法的返回值为另一个流,只是对操作进行了记录,并不会立即执行
    1. 无状态
    //过滤Stream中所有不符合predicate的元素
    filter(Predicate predicate)
    //使用ToXxxFunction对流中元素执行一对一的转换,该方法返回的新流中包含了ToXxxFunction转换生成的所有元素
    mapToXxx(ToXxxFunction mapper)
    //依次对每个元素执行一些操作(比如修改元素属性值),该方法返回的流与原有流包含相同的元素,该方法主要用于调试,与mapToXxx方法不同为,方法接收的对象一个为ToXxxFunction,另一个为Consumer,这两个对象中需被重写的方法,返回值一个为void,一个为boolean
    peek(Consumer action)
    
    1. 有状态:会给流增加一些新的属性,比如元素的唯一性、元素最大数量、保证元素以排序方式被处理等,有状态方法需要更大的性能开销
    //用于排序流中所有重复元素,判断重复的标准是equals返回true、hashCode方法相等
    distinct()
    //用于保证流中的元素在后续访问中处于有序状态
    sorted()
    //用于保证对该流的后续访问中最大允许访问的元素个数
    limit(long maxSize)
    
  2. 终止操作(terminal):末端方法后,流被"消耗",不可再用
    1. 非短路
    //遍历流中所有元素,对每个元素执行action
    forEach(Consumer action)
    //将流中所有元素转换为一个数组
    toArray()
    //该方法有三个重载版本,都用于通过某种操作来合并流中元素
    reduce()
    //返回流中所有元素的最小值/最大值/数量
    min()/max()/count()
    //接收一个Collector实例,将流中元素收集成另外一个数据结构
    collect()
    
    1. 短路:遇到符合条件的元素就结束整个操作,不再对剩余元素处理
    //判断流中是否包含至少有一个/全/没有一个符合predicate的条件
    any/all/noneMatch(Predicate predicate)
    //返回流中第/任意一个元素
    findFirst/Any()
    
8.2.4.4 方法示例
  1. 中间方法代码
import java.io.FileNotFoundException;
import java.util.stream.Stream;

public class Student {
	int age;

	public Student(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		return true;
	}

	public static void main(String[] args) throws FileNotFoundException {
		// 6 4 6 7 3 9 8 10 12 14 14
		Stream<Student> stream = Stream.of(new Student(6), new Student(4), new Student(6), new Student(7),
				new Student(3), new Student(9), new Student(8), new Student(10), new Student(12), new Student(14),
				new Student(14));
		// 6 6 7 9 8 10 12 14 14 注意此处如果补重写hashCode与equals方法,将得不到想要的结果,因为不同Student对象是不同的
		Stream<Student> newStream = stream.filter(s -> s.age > 5)
				// 6 7 9 8 10 12 14
				.distinct()
				// 9 8 10 12 14
				.skip(2)
				// 9 8
				.limit(2)
				// 10 9
				.peek(c -> c.age = c.age + 1);
		// 9 10 利用map转为了新的只包含Integer元素的流
		Stream<Integer> intStream = newStream.map(c -> c.age).sorted();
		intStream.forEach(System.out::println);

	}
}
  1. 末端方法代码
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class TerminalTest {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

		boolean allMatch = list.stream().allMatch(e -> e > 10); // false
		boolean noneMatch = list.stream().noneMatch(e -> e > 10); // true
		boolean anyMatch = list.stream().anyMatch(e -> e > 4); // true

		Integer findFirst = list.stream().findFirst().get(); // 1
		Integer findAny = list.stream().findAny().get(); // 1

		long count = list.stream().count(); // 5
		Integer max = list.stream().max(Integer::compareTo).get(); // 5
		Integer min = list.stream().min(Integer::compareTo).get(); // 1

		// 第一次执行,以第一个元素作为x1,第二个元素作为x2,第二次执行时候,以上一次结果(x1+x2)作为x1,第三个元素作为x2,依此类推
		Integer v = list.stream().reduce((x1, x2) -> x1 + x2).get();
		// 1+2+3+4+5
		System.out.println(v);

		List<Student> listStudent = Arrays.asList(new Student(2), new Student(2), new Student(3), new Student(4),
				new Student(5));

		List<Student> ageList = listStudent.stream().collect(Collectors.toList());
		// [Student [age=1], Student [age=2], Student [age=3], Student [age=4], Student
		// [age=5]]
		System.out.println(ageList);
		Set<Student> ageSet = listStudent.stream().collect(Collectors.toSet());
		// [Student [age=2], Student [age=3], Student [age=4], Student [age=5]]
		// 少了一个元素,因为Set有去重功能
		System.out.println(ageSet);

		// toMap方法中需要传入两个函数式接口,这两个函数式接口的方法需要有返回值,不能是void
		// 下方方法执行时报错,因为Map的key不能相同,但上面listStudent,有age相同的对象,所以转换时失败
		Map<Integer, Student> ageMap = listStudent.stream().collect(Collectors.toMap(c -> c.age, c -> c));
		System.out.println(ageMap);
	}
}

8.3 Set

不可重复插入

8.3.1 HashSet
  1. 重复判断标准:equals返回true且hashCode方法返回值相同
  2. 当调用了HashSet的add方法存放obj时,HashSet先调用obj的hashCode方法,得到一个hashCode值,将这个值转为一个数组下标,该下标标记了obj的位置,如果这个位置上的链表中没有元素,就把obj对象添加到链表上,如果链表中已有元素,则遍历该链表,调用obj的equals方法,判断obj是否和链表上的某个元素重复,不重复则添加,重复则不添加
  3. equals相等,hashCode值不等:两个对象都可以插入,且存放在不同的位置,但HashSet原则上不想插入相等的元素
  4. equals不等,hashCode相等:两个对象会被放到同一位置的链表上,影响性能
  5. 重写hashCode方法原则:
    1. hashCode应尽量与equals方法返回的结果一致
    2. 对象中用于equals方法中的实例变量,都应用于计算hashCode值
    3. 同一对象多次调用hashCode方法返回值应该相同
  6. 重写hashCode具体步骤:
    1. 把对象中每个参与equals方法的实例变量都计算出一个int类型的hashCode值,计算方式在p294-图8.1
    2. 用上一步算出的多个hashCode值组合计算出一个hashCode值并返回,为避免直接相加的偶然相等,可将各个实例变量的hashCode值再乘一个质数后再相加
  7. 向HashSet中添加可变对象时,尽量不要修改对象的参与equals,hashCode计算的实例变量的值,这可能导致HashSet无法准确访问该对象
例:HashSet中存放R类型的元素,该类中有成员变量count参与了equals和hashCode的计算。元素r1(count:-2)r2(count:-3),r3(count:5),r4(count:9)
//1.将第r1的count值由-2改为-3,此时调用方法remove(new R(-3));系统首先计算R(-3)对象的hashCode值,找其存放位置,然后将此位置对象用equals方法与R(-3)进行比较,相等即删除,此时r2被删除
//2.当调用contains(new R(-3))时,返回false,原理与上相同
8.3.2 LinkedHashSet
  1. 重复判断标准:equals返回true且hashCode方法返回值相同
  2. 按插入顺序排序
  3. HashSet子类,也根据hashCode值决定存储的位置,但同时使用链表维护元素次序,当遍历LinkedHashSet中元素时,会按元素插入顺序访问集合中元素
8.3.3 TreeSet
  1. 重复判断标准:Comparable的compareTo方法或Comparator的compare方法返回0
  2. 有序,分为定制排序与自然排序,自然排序下不可以插入null,因为会调用插入元素的compareTo方法
  3. SortedSet的实现类,TreeSet可确保集合元素处于排序状态
  4. TreeSet方法
Comparator comparator():如果TreeSet采用定制排序,返回定制排序所使用的的Comparator,如果为自然排序,返回null
Object first():返回第一个元素
Object last():返回最后一个元素
Object lower(Object e):返回小于e的最大元素,e无需在集合中
Object higher(Object e):返回大于e的最小元素,e无需在集合中
SortedSet subSet(Object fromEle,object toEle):将集合中fromEle到toEle(不包含)范围的元素作为子集合返回
SortedSet headSet(object toEle):返回toEle之前的所有元素所组成的子集
SortedSet tailSet(object fromEle):返回元素fromEle之后的所有元素的子集
  1. 自然排序与定制排序
    1. 自然排序(升序):
      1. 只有实现了Comparable接口的类的对象才可以以自然排序的方式放入TreeSet中,否则引发ClassCastException。因为如果不继承该类,则没有compareTo方法,自己在类中定义compareTo方法无效
      2. TreeSet会调用集合元素实现的Comparable类的compareTo(T o)方法来比较元素大小,然后按元素升序排列
      3. obj1.compareTo(obj2)方法返回一个整数,0表示两者相等,正整数表示obj1>obj2,负数表示obj1<obj2
      4. 如果希望TreeSet正常工作,TreeSet中只能添加同一类型的对象,因为大部分类在实现compareTo(Object obj)方法时,都需将被比较对象obj强制转换成相同类型,因为只有相同类型的两个实例才会比较大小,当将对象放入TreeSet时,TreeSet会调用这个被放入对象的compareTo方法,与集合中其他元素进行比较,并根据红黑树结构找到其存储位置
      5. 重写对象的compareTo方法应保证该方法与equals方法返回相同的结果,否则会造成明明"不同"的对象却无法被插入到TreeSet中
      6. TreeSet只能删除没有被修改实例变量,且不与其他被修改实例变量的对象重复的对象
    2. 定制排序
      1. 可在创建TreeSet时传入Comparator接口的对象,并重写其int compare(T obj1,T obj2)方法,如该方法返回正整数,表明obj1大于obj2,返回0表示obj1等于obj2,返回负整数表示obj1小于obj2,该方法负责对集合中元素进行排序,按compare方法的结果将集合中元素从小到大排序
      TreeSet ts = new TreeSet(new Comparator() {
      	@Override
      	public int compare(Object o1, Object o2) {
      	    //如果返回0表示任意两个元素都相等,即永远无法插入第二个元素
      		return 0;
      	}
      });
      
      1. 此时向集合ts中添加的元素不用再实现Comparable接口,因为此时不再使用对象的compareTo方法进行排序,而是调用Comparator中重写的compare方法排序
8.3.4 EnumSet
  1. 重复判断标准:同一枚举对象
  2. 按枚举值定义顺序进行排序,不可插入空(因为枚举值不可以为空)
  3. EnumSet中的元素必须是指定枚举类型的枚举值
  4. EnumSet为抽象类,不能通过构造器创建对象,但可以通过其提供的类方法创建其子类的实例,以下介绍EnumSet的所有类方法
EnumSet allOf(class elementType):将枚举类型为elementType的所有枚举值组成集合并返回
EnumSet complementOf(EnumSet s):返回S中不包含的所有枚举值的EnumSet
EnumSet copyOf(Collection c):使用普通集合创建EnumSet,该普通集合中所有元素必须为同一枚举类型的枚举值
EnumSet copyOf(EnumSet s):创建与s相同的新EnumSet
EnumSet noneOf(Class elementType):创建元素类型为elementType的枚举值的空EnumSet
EnumSet of(E first ,E ... rest):创建一个包含一个或多个枚举值的EnumSet集合,传入的多个枚举值必须属于同一个枚举类
EnumSet range(E from ,E to):创建包含从from枚举值到to枚举值范围内所有枚举值的EnumSet
8.3.5 Set实现类性能分析
  1. EnumSet最快,但只能保存同一枚举类下的枚举值作为集合元素
  2. HashSet其次,但无序,LinkedHashSet插入删除慢,但遍历快
  3. TreeSet更次,但可以按任意方式排序,做范围查询时候较快
  4. 以上三个实现类均线程不安全,如果多个线程访问同一个Set,必须手动保证Set集合的同步,通常可用Collections工具类的synchronizedSortedSet来包装集合

8.4 List

可以通过索引来访问指定位置的集合元素,List默认按元素的添加顺序设置元素的索引,第一次添加的元素的索引为0,第二次为1,以此类推

8.4.1List接口与ListIterator接口
  1. List作为Collection接口的子接口,拥有其全部方法,由于List有序,所以增加了根据索引操作集合元素的方法
void add(int index,Object element):将元素element插入到List集合的index处,index最多比List中现有的最大index大1,否则报错,如果在中间index处插入元素,其后所有元素后移1boolean addAll(int index,Collection c):将集合c所包含的所有元素插入到index处
Object get(int index):返回指定index处元素
int indexOf(Object o):返回o在集合中第一次出现的位置的索引
int lastIndexOf(Object o):返回o在集合中最后一次出现的位置的索引
Object remove(int index):删除并返回index索引处元素
//Collection接口中有remove(Object o)方法,虽然int为Object子类,但不构成重写,形参列表只要不完全相同,就不构成重写,即如果传入int型,会调用remove(int index),传入其他类型时,先自动转型为Object,然后调用remove(Object o)方法
Object set(int index,Object element):将index处索引元素替换为element,并返回旧元素,此index必须为集合中有效索引,不会增加集合的长度
List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex的子集合,包含fromIndex不包含toIndex
  1. List可以使用普通for循环来遍历集合
  2. List判断两个对象相等的标准为通过equals方法返回true
books.remove(new A()):会调用new A()这个对象的equals方法,从索引0处开始与集合中所有元素进行比较,找到第一个返回true的地方,将其删除
  1. List额外提供了一个listIterator方法,返回一个ListIterator对象,ListIterator接口继承Iterator接口,增加了一些专门操作List集合的方法
boolean hasPrevious():返回迭代器关联的集合是否还有上一个元素
Object previous():返回迭代器的上一个元素
void add(Object o):上一次迭代元素后添加元素o,迭代器初始指向索引为-1的元素,先使用正向迭代(next())方法到几个元素后,hasPrevious方法才会返回true
8.4.1 ArrayList与Vector实现类
  1. ArrayList与Vector都是基于数组实现的List类,即封装了一个动态的允许再分配的Object[]数组
  2. ArrayList和Vector对象使用initialCapacity参数来设置该数组长度,当添加元素个数超出该长度,他们的initialCapacity自动增加,默认为10,ArrayList线程不安全
  3. 固定长度的List:Arrays.asList(Object a,Object b,…):该方法将传入对象转换成一个List集合,但这个集合不是ArrayList或Vector的实例,而是Arrays的内部类ArrayList的实例,是一个固定长度的List集合,如果试图删除或增加该集合中元素,会引发UnsupportedOperationException

8.5 Queue接口

  1. Queue模拟队列这种数据结构,队列通常指尾插头出的容器,即新元素插入(offer)到队列的尾部,访问(poll)元素会返回队列的头部,通常不允许随机访问队列中的元素
  2. Queue定义了如下方法
boolean offer(Object e):与add功能相同,越界返回false
void add(Object e):将指定元素插入到队列的尾部,越界报错
Object poll():获取队列头部的元素,删除该元素,如果队列中没有元素,返回null
Object remove():获取队列头部的元素,删除该元素,如果队列中没有元素,报错
boolean peek():获取队列头部的元素,不删除该元素,如果队列中没有元素,返回null
Object element():获取队列头部的元素,不删除该元素,如果队列中没有元素,报错

8.5.1 PriorityQueue实现类
  1. PriorityQueue中元素按由小到大重新排序,即用peek或poll取元素时,并不是先取出最先放入的元素,而是取出队列中最小的元素
  2. PriorityQueue对象的toString方法返回"["+所有元素以逗号分隔+"]",这些元素不一定按从小到大排序,但多次调用poll方法既可看到元素按从小到大顺序移除队列
  3. 有自然排序和定制排序,与TreeSet细节相同
8.5.2 Deque接口与ArrayDeque实现类
  1. Deque为Queue子接口,代表一个双端队列,Deque接口里定义了一些双端队列的方法,允许两端来操作队列中的元素
void addFirst(Object e):将e插入该双端队列的头(注意不是两头都加)
void addLast(Object e):将e插入该双端队列的尾
void push(Object e):为栈方法,同addFirst
boolean offerFirst(Object e):
boolean offerLast(Object e):
Iterator descendingIterator():返回该双端队列对应的迭代器,该迭代器以逆向顺序迭代队列中的元素
Object getFirst():获取头部元素,不删除,为空报错
Object getLast():获取尾部元素,不删除,为空报错
Object peekFirst():获取头部元素,不删除,为空返回null
Object peekLast():获取尾部元素,不删除,为空返回null
Object pollFirst():获取头部元素,删除,为空返回null
Object pollLast():获取尾部元素,删除,为空返回null
Object removeFirst():获取头部元素,删除,为空报错
Object removeLast():获取尾部元素,删除,为空报错
Object pop():为栈方法,同removeFirst
boolean removeFirstOccurrence(Object o):删除第一次出现的元素o
boolean removeLastOccurrence(Object o):删除最后一次出现的元素o

  1. 栈这种数据结构指头插头出,即新元素插入到栈的顶部,访问元素返回栈的顶部
  2. ArrayDeque为Deque实现类,是基于数组实现双端队列,与ArrayList相近,同样可以指定numElements参数来指定Object[]的长度,默认16
  3. ArrayDeque由于支持从头插入,从头取值,所以可以模拟栈的行为,也可以模拟队列的行为,一般用ArrayDeque来代替Vector的Stack子类
8.5.3 LinkedList实现类

LinkedList实现List接口,所以可以根据索引随机访问集合中的元素,同时也实现了Deque接口,所以也可以被当做双端队列使用

8.5.4 各种线性表的性能分析
  1. ArrayList访问快,插入慢,LinkedList以链表作为底层实现集合,访问慢,插入快,总体来说ArrayList性能更好,插入速度:hash>linked>array,访问速度:array>linked>hash
  2. 遍历ArrayList用get(i)更快,遍历LinkedList用Iterator更快
  3. 多线程访问集合中元素,应用Collections包装集合

8.6 Map

8.6.1 Map理解与基本方法
  1. Map中保存两组值,一组值可以组成Set集合,用于保存Map中的key,另一组值可以组成List集合,用于保存Map中的value,key和value都可以是任何数据类型
  2. java先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合,因此所有key放在一起就组成了一个Set集合,Map的key不允许重复,因此Set集合不允许插入重复数据
  3. Map中所有value放在一起类似一个List,元素间可以重复,每个元素都通过索引进行查找,只不过不再使用整数值作为索引,而使用key作为索引
  4. Map接口方法
void clear():删除所有key-value对
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
Set entrySet():返回Map中key-value对所组成的Set集合,因为Map中key不允许重复,所以一定可以组成一个Set集合,每个元素都是Map的内部类Entry的对象
Object get(Object key):返回key所对应的value,都不包含此key,返回null
boolean isEmpty():Map中是否没有元素(不包含任何key-value对)
Set keySet():返回所有key组成的Set集合
Object put(Object key,Object value):添加一个key-value对,如果原来已有该key,则新value覆盖原value
void putAll(Map m):将m中所有key-value对放入集合中
Object remove(Object key):删除key对应的key-value对,返回value值,如果key不存在,返回null
int size():返回key-value对个数
Collection values():返回所有values组成的Collection
  1. Map内部类Entry的方法:通过Map的entrySet得到Entry的Set集合,之后Iterator进行遍历即可得到每个Map.Entry对象
Object getKey():返回Entry中的key值
Object getValue():返回Entry中的value值
Object setValue(V value):设置Entry中的value值,并返回新value值
  1. for-each遍历Map集合
Map map = new HashMap();
for(Object key : map.keySet()){
	System.out.println(key+"-->"+map.get(key));
}   
  1. 所有Map的实现类都重写了toString()方法,总返回如下格式字符串:
{key1==value1,key2==value2,...}
8.6.2 HashMap与Hashtable实现类:
  1. HashMap线程不安全,Hashtable线程安全但性能低,Hashtable拥有方法elements(类似于Map中的values()),keys(类似于keySet()),Hashtable不允许null作为key(会调用key的hashCode方法)和value(写死的,如果为空就抛异常,怀疑和多线程有关),HashMap就可以
  2. Map中key不可以重复,HashMap与Hashtable中对key的要求与HashSet对元素的要求完全相同,而对于方法containsValue(),判断value相等的条件为equals方法比较返回true
8.6.3 LinkedHashMap实现类:

与LinkedHashSet相同

8.6.4 Properties实现类:用于读写配置文件
  1. Properties为Hashtable子类,该类可把Map对象和属性文件关联起来,从而将Map中的key-value对写入属性文件,也可以将属性文件中"属性名=属性值"加载到Map对象中,注意key与value都应该是String类型
  2. Properties提供的方法
String getProperty(String key):获取Properties中指定属性名对应属性值,类似于Map的get(Object key)
String getProperty(String key,String default):获取Properties中指定属性名对应属性值,如果不存在指定key,返回default
Object setProperty(String key,String value):类似Map的put(),不会改变文件本身
void load(InputStream inStream):从属性文件(以输入流表示)加载key-value对,追加到Properties对象中,无法保证顺序,文件格式必须为key=value
void store(OutputStream out ,String comments):将comments内容输出到文件中,前面加#
  1. Properties可以将key-value对以XML文件格式保存,也可以从XML中加载key-value
8.6.5 SortedMap接口和TreeMap实现类
  1. 对key的要求与TreeSet对元素的要求完全相同
  2. TreeMap方法
Map.Entry firstEntry():返回最小key所对应的key-value对,如果Map为空,返回null
Map.Entry lastEntry():返回最大key所对应的key-value对,如果Map为空,返回null
Object firstKey():返回最小key,如果Map为空,返回null
Object lastKey():返回最大key,如果Map为空,返回null
Map.Entry higherEntry(Object key):返回最大于key的最小key所对应的key-value对
Object higherKey(Object key):
Map.Entry lowerEntry(Object key):
Object lowerKey(Object key):
navigableMap subMap(Object fromKey,boolean fromInclusive,Object toKey,boolean toInclusive):返回Map的子Map,是否包含由第二和第四个元素决定
SortedMap subMap(Object fromKey,Object toKey):
SortedMap tailMap(Object fromKey):
navigableMap tailMap(Object fromKey,boolean inclusize):
SortedMap headMap(Object toKey):
navigableMap headMap(Object toKey,boolean inclusize):

8.6.6 WeakHashMap实现类
  1. 与HashMap用法基本相似,一般用于缓存的实现
  2. 弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收
  3. HashMap的key保留对实际对象的强引用,所以只要HashMap对象不被销毁,key对象永远不会被垃圾回收,所以key-value也不会被垃圾回收,而WeakHashMap只保留key对象弱引用,如果key对象没被其他强引用对象所引用,则这些key会被回收,其对应的key-value对也会被回收
  4. 更直观的说,当使用 WeakHashMap 时,即使没有显示的添加或删除任何元素,也可能发生如下情
    1. 调用两次size()方法返回不同的值
    2. 两次调用isEmpty()方法,第一次返回false,第二次返回true
    3. 两次调用containsKey()方法,第一次返回true,第二次返回false,尽管两次使用的是同一个key
    4. 两次调用get()方法,第一次返回一个value,第二次返回null,尽管两次使用的是同一个对象
  5. 例:key为new String(“语文”),是匿名字符串对象,无强引用引用他,所以会被回收,key为"语文"是字符串直接量,系统会自动保存对该字符串对象的强引用,所以不会被回收
  6. 不应该让key对象具有任何强引用,否则会失去WeakHashMap的意义
8.6.7 IdentityHashMap实现类:
  1. 与HashMap用法相似
  2. IdentityHashMap判断key值相等的条件与HashMap(equals返回true且hashCode值相同)不同,必须严格相等才算相等(==返回true)
8.6.8 EnumMap实现类:
  1. 对key的要求与EnumSet对元素的要求一致
  2. EnumMap a = new EnumMap(Season.class),即key必须为枚举类Season的枚举值
8.6.9 Map实现类的性能分析:

与Set相同,一般使用HashMap

8.7 操作集合的工具类:Collections

8.7.1 排序操作
static void reverse(List list):反转list中的元素顺序
static void shuffle(List list):list中元素随机排序,模拟洗牌动作
static void sort(List list):自然顺序对list升序排序
static void sort(List list,Comparator c):根据c对list升序排序
static void swap(List list,int i,int j):交换i,j处元素
static void rotate(List list,int distance):如果distance为正,将后distance个元素放在前面,如果distance为负,将前distance元素放在后面
8.7.2 查找与替换
static int binarySearch(List list,Object o):以二分法搜索list,返回元素o的索引,List中值必须处于有序状态,否则返回值不正确,元素多时,必indexOf效率高
static Object max(Collection coll):根据元素自然排序,返回最大元素
static Object max(Collection coll,Comparator comp):根据comp排序,返回最大元素
static Object min(Collection coll):根据元素自然排序,返回最小元素
static Object min(Collection coll,Comparator comp):根据comp排序,返回最小元素
static void fill(List list,Object obj):用obj替换list中所有元素
static int frequency(Collection c,Object o):返回o出现次数
static int indexOfSubList(List source,List target):返回target集合在source中第一次出现的位置索引,如果没有出现过,返回-1
static int lastIndexOfSublist(List source,List target):
static boolean replaceAll(List list,Object oldVal,Object newVal):使用newVal替换list中所有oldVal元素
8.7.3 同步控制:
//实现原理:
//1. 将集合转为了一个SynchronizedCollection对象,该对象为Collections工具类中的一个静态内部类,该内部类中成员变量c保存传入的集合,成员变量mutex保存自身,且这两个成员变量为final定义,不允许指向别处
//2. 当对该对象做add操作时,synchronized (mutex),即同一时间,只有一个原集合可以进行add操作,因此即使该SynchronizedCollection对象传给不同线程,同一时间,只能有一个线程对其进行操作
Collection c = Collections.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set set = Collections.synchronizedSet(new HashSet());
Map map = Collections.synchronizedMap(new HashMap());
8.7.4 设置不可变集合:即只能访问集合元素,但不能修改集合元素
static Xxxx emptyXxxx():返回空的不可改变的集合对象,返回值可以为List,Set,SortedSet,Map,SortedMap等等
static Xxxx singletonXxxx(Object o):返回只包含元素o的不可变的集合
static Xxxx unmodifiableXxxx(Collection/Map o):返回包含o集合中所有元素的不可变的集合

8.8繁琐的接口:Enumeration

  1. Enumeration作用与Iterator相同
  2. Enumeration几乎只能迭代Vector,Hashtable这种古老的集合
  3. Vector有elements方法获取Enumeration对象,Hashtable有elements和keys方法
boolean hasMoreElements():
Object  nextElement():
发布了32 篇原创文章 · 获赞 0 · 访问量 948

猜你喜欢

转载自blog.csdn.net/hanzong110/article/details/102488709