8.1 集合概述
- 为保存数量不确定的数据,以及保存具有映射关系的数据,java提供了集合类
- 继承关系:红色部分为线程安全容器
- Set、List、Queue、Map
- Set:不能重复,无法从指定位置插入取出元素,有序或无序
- List:可以重复,可以从在指定位置插入取出元素,有序
- Queue:可以重复,只能从头尾插入取出元素,模拟队列,有序
- Map:key放在一起为Set集合,value放在一起为Collection
8.2 Collection与Iterator接口
8.2.1 Collection方法:增删改查转
//集合中添加元素。一定注意,将元素插入集合失败不会报错,而是返回false
boolean add(Object o)
//集合中添加集合
boolean addAll(Collection c)
//删除元素o
boolean remove(Object o)
//删除c集合中所包含的元素
boolean removeAll(collection c)
//将符合filter条件的元素从集合中删除
//Predicate翻译过来叫做谓语(是、不是),或叫断言,实际上就是一个函数式接口,该函数式接口包含一个abstract的test方法,返回值为boolean类型。集合会将元素传给Predicate对象的test方法,返回值为true时,该元素被删除
boolean removeIf(Predicate filter)
//删除原集合中,与c没有交集的所有元素
boolean retainAll(collection c)
//清空集合
void clear()
//返回集合中元素个数
int size()
//集合长度是否为0
boolean isEmpty()
//集合中是否包含元素o
boolean contains(Object o)
//结合中是否包含c集合中所有元素
boolean containsAll(Collection c)
//返回Iterator对象来遍历集合中的元素
Iterator iterator()
//集合转数组
Object[] toArray()
//返回以该集合中元素作为元素的流
Stream<E> stream()
8.2.2 遍历集合中元素
- foreach循环遍历集合中元素
- foreach循环的本质是调用this对象的iterator方法获取迭代器,使用迭代器循环Collection或数组中每一个元素
- 与Iterator循环相同,不可以用Collection对象的remove方法对集合进行改变
- 由于foreach中没有Iterator的remove方法,所以foreach循环只能遍历集合中元素,无法修改集合中元素
- Iterator遍历集合
//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();
}
//方法二:
//该方法内部使用hashNext、next,对集合中所有元素进行循环,并做action操作
//default void forEachRemaining(Consumer<? super E> action)
it.forEachRemaining(c -> System.out.println("迭代集合中元素:" + c));
}
- Iterable遍历集合:Collection继承了Iterable类,拥有其forEach(Consumer action)方法,其内部相当于调用Iterable内部的iterator方法获取迭代器,从而循环Collection的所有元素,做action操作
Collection books = new HashSet();
...
books.forEach(c -> System.out.println("迭代集合中元素:" + c));
8.2.3 Stream操作集合
Java8新增
8.2.3.1 Stream简介
- Stream:
- 同一个高级版本的 迭代器(Iterator)
- 代表数据元素有限/无限的顺序,它不是数据结构,不会保存数据
- 是流式的:即所有对元素的操作可以链接成一个表达式
- 不会修改原来的数据源,它会将操作后的数据保存到新对象中
- Stream中的泛型表示该流中存放的元素类型
- Stream pipeline:
- 表示数据元素的一个多级的运算
- 包含一个源Stream,0个/多个中间操作,和一个终止操作
- 终止操作是单向的,且不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返
- 是lazy的,即调用终止操作时,才开始计算,对于终止操作不需要的数据元素,永远不会计算。
- 可以并行,但通常不建议
8.2.3.2 创建Stream对象
- Collection.stream/parallelStream():集合转为流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
- Arrays.stream():数组转为流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
- Stream.of/iterate/generate():流的静态方法
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
//iterate第一个参数为Stream中的第一个元素,第二个参数为如何通过第一个元素产生后续元素的迭代方案
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
//generate方法中传入生产元素的方法
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
- BufferedReader.lines():将IO流,每一行对应的字符串作为一个元素,转为流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
- 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.3.3 Stream中方法分类
- 中间操作(intermediate):中间方法的返回值为另一个流,只是对操作进行了记录,并不会立即执行
- 无状态
//过滤Stream中所有不符合predicate的元素,组成新流 filter(Predicate predicate) //按mapper的规则,将流中所有元素转换为其他对象,用这些新对象组成流 map(Function mapper) //按mapper的规则,将流中所有元素转换为基本类型,用这些基本类型变量组成流 mapToInt/Long/Double(ToInt/Long/DoubleFunction mapper) //按action的规则,修改流中元素的属性,并组成新流 //该方法主要用于调试,与mapToXxx方法不同为,方法接收的对象一个为ToXxxFunction,另一个为Consumer,这两个对象中需被重写的方法,返回值一个为void,一个为boolean peek(Consumer action) //跳过前n个元素,组成新流 skip(Long n)
- 有状态:会给流增加新的属性,例如流中"最多元素个数"、“不允许有重复元素”、“必须按顺序排序”。有状态方法需要更大的性能开销
//用于保证对流的后续访问中,只对重复元素访问一次,判断重复的标准是equals返回true、hashCode方法相等 distinct() //用于保证流中的元素在后续访问中处于有序状态 sorted() //用于保证对该流的后续访问中最大允许访问的元素个数 limit(long maxSize)
- 终止操作(terminal):末端方法后,流被"消耗",不可再用
- 非短路
//遍历流中所有元素,对每个元素执行action forEach(Consumer action) //将流中所有元素转换为一个数组 toArray() //该方法有三个重载版本,都用于通过某种操作来合并流中元素 reduce(BinaryOperator accumulator) //返回如果流中元素按comparator排序时,最小值/最大值 min/max(Comparator comparator) //数量 count() //接收一个Collector实例,将流中元素收集成另外一个数据结构 collect(Collector collector)
- 短路:遇到符合条件的元素就结束整个操作,不再对剩余元素处理
//判断流中是否包含至少有一个/全/没有一个符合predicate的条件 any/all/noneMatch(Predicate predicate) //返回流中第/任意一个元素 findFirst/Any()
8.2.3.4 方法示例
- 中间操作示例
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);
}
}
- 终止操作代码
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
- 重复判断标准:equals返回true且hashCode方法返回值相同
- 当调用了HashSet的add方法存放obj时,HashSet先调用obj的hashCode方法,得到一个hashCode值,将这个值转为一个数组下标,该下标标记了obj的位置,如果这个位置上的链表中没有元素,就把obj对象添加到链表上,如果链表中已有元素,则遍历该链表,调用obj的equals方法,判断obj是否和链表上的某个元素重复,不重复则添加,重复则不添加
- equals相等,hashCode值不等:两个对象都可以插入,且存放在不同的位置,但HashSet原则上不想插入相等的元素
- equals不等,hashCode相等:两个对象会被放到同一位置的链表上,影响性能
- 重写hashCode方法原则:
- hashCode应尽量与equals方法返回的结果一致
- 对象中用于equals方法中的实例变量,都应用于计算hashCode值
- 同一对象多次调用hashCode方法返回值应该相同
- 重写hashCode具体步骤:
- 把对象中每个参与equals方法的实例变量都计算出一个int类型的hashCode值,计算方式在p294-图8.1
- 用上一步算出的多个hashCode值组合计算出一个hashCode值并返回,为避免直接相加的偶然相等,可将各个实例变量的hashCode值再乘一个质数后再相加
- 向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
- 重复判断标准:equals返回true且hashCode方法返回值相同
- 按插入顺序排序
- HashSet子类,也根据hashCode值决定存储的位置,但同时使用链表维护元素次序,当遍历LinkedHashSet中元素时,会按元素插入顺序访问集合中元素
8.3.3 TreeSet
- 重复判断标准:Comparable的compareTo方法或Comparator的compare方法返回0
- 有序,分为定制排序与自然排序,自然排序下不可以插入null,因为会调用插入元素的compareTo方法
- SortedSet的实现类,TreeSet可确保集合元素处于排序状态
- 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之后的所有元素的子集
- 自然排序与定制排序
- 自然排序(升序):
- 只有实现了Comparable接口的类的对象才可以以自然排序的方式放入TreeSet中,否则引发ClassCastException。因为如果不继承该类,则没有compareTo方法,自己在类中定义compareTo方法无效
- TreeSet会调用集合元素实现的Comparable类的compareTo(T o)方法来比较元素大小,然后按元素升序排列
- obj1.compareTo(obj2)方法返回一个整数,0表示两者相等,正整数表示obj1>obj2,负数表示obj1<obj2
- 如果希望TreeSet正常工作,TreeSet中只能添加同一类型的对象,因为大部分类在实现compareTo(Object obj)方法时,都需将被比较对象obj强制转换成相同类型,因为只有相同类型的两个实例才会比较大小,当将对象放入TreeSet时,TreeSet会调用这个被放入对象的compareTo方法,与集合中其他元素进行比较,并根据红黑树结构找到其存储位置
- 重写对象的compareTo方法应保证该方法与equals方法返回相同的结果,否则会造成明明"不同"的对象却无法被插入到TreeSet中
- TreeSet只能删除没有被修改实例变量,且不与其他被修改实例变量的对象重复的对象
- 定制排序
- 可在创建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; } });
- 此时向集合ts中添加的元素不用再实现Comparable接口,因为此时不再使用对象的compareTo方法进行排序,而是调用Comparator中重写的compare方法排序
- 自然排序(升序):
8.3.4 EnumSet
- 重复判断标准:同一枚举对象
- 按枚举值定义顺序进行排序,不可插入空(因为枚举值不可以为空)
- EnumSet中的元素必须是指定枚举类型的枚举值
- 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实现类性能分析
- EnumSet最快,但只能保存同一枚举类下的枚举值作为集合元素
- HashSet其次,但无序,LinkedHashSet相当于比HashSet多建了一个链表,用于维护集合中元素顺序,因此插入删除慢,但遍历快
- TreeSet更次,但可以按任意方式排序,做范围查询时候较快
- 以上三个实现类均线程不安全,如果多个线程访问同一个Set,必须手动保证Set集合的同步,通常可用Collections工具类的synchronizedSet来包装集合
8.4 List
可以通过索引来访问指定位置的集合元素,List默认按元素的添加顺序设置元素的索引,第一次添加的元素的索引为0,第二次为1,以此类推
8.4.1List接口与ListIterator接口
- List作为Collection接口的子接口,拥有其全部方法,由于List有序,所以增加了根据索引操作集合元素的方法
//将元素element插入到List集合的index处,index最多比List中现有的最大index大1,否则报错,如果在中间index处插入元素,其后所有元素后移1位
void add(int index,Object element)
//将集合c所包含的所有元素插入到index处
boolean addAll(int index,Collection c)
//Collection接口中有remove(Object o)方法,虽然int为Object子类,但不构成重写,形参列表只要不完全相同,就不构成重写,即如果传入int型,会调用remove(int index),传入其他类型时,先自动转型为Object,然后调用remove(Object o)方法
//删除并返回index索引处元素
Object remove(int index)
//将index处索引元素替换为element,并返回旧元素,此index必须为集合中有效索引,不会增加集合的长度
Object set(int index,Object element)
//返回指定index处元素
Object get(int index)
//返回o在集合中第一次出现的位置的索引
int indexOf(Object o)
//返回o在集合中最后一次出现的位置的索引
int lastIndexOf(Object o)
//返回从fromIndex到toIndex的子集合,包含fromIndex不包含toIndex
List subList(int fromIndex,int toIndex)
- List可以使用普通for循环来遍历集合
- List判断两个对象相等的标准为通过equals方法返回true
books.remove(new A()):会调用new A()这个对象的equals方法,从索引0处开始与集合中所有元素进行比较,找到第一个返回true的地方,将其删除
- List额外提供了一个listIterator方法,返回一个ListIterator对象,ListIterator接口继承Iterator接口,增加了一些专门操作List集合的方法
//返回迭代器关联的集合是否还有上一个元素
boolean hasPrevious()
//返回迭代器的上一个元素
Object previous()
//上一次迭代元素后添加元素o,迭代器初始指向索引为-1的元素,先使用正向迭代(next())方法到几个元素后,hasPrevious方法才会返回true
void add(Object o)
8.4.1 ArrayList与Vector实现类
- ArrayList与Vector都是基于数组实现的List类,即封装了一个动态的允许再分配的Object[]数组
- ArrayList和Vector对象使用initialCapacity参数来设置该数组长度,当添加元素个数超出该长度,他们的initialCapacity自动增加,默认为10,ArrayList线程不安全
- 固定长度的List:Arrays.asList(Object a,Object b,…):该方法将传入对象转换成一个List集合,但这个集合不是ArrayList或Vector的实例,而是Arrays的内部类ArrayList的实例,是一个固定长度的List集合,如果试图删除或增加该集合中元素,会引发UnsupportedOperationException
8.5 Queue接口
- Queue模拟队列这种数据结构,队列通常指尾插头出的容器,即新元素插入(offer)到队列的尾部,访问(poll)元素会返回队列的头部,通常不允许随机访问队列中的元素
- Queue定义了如下方法
//插入元素到队尾,越界返回false
boolean offer(Object e)
//插入元素到队尾,越界报错
boolean add(Object e)
//获取队列头元素,并删除,队列中无元素返回null
Object poll()
//获取队列头元素,并删除,队列中无元素报错
Object remove()
//获取队列头元素,不删除,队列中无元素返回null。即队列中元素不变,多次调用peek返回值相同
Object peek()
//获取队列头元素,不删除,队列中无元素报错
Object element()
8.5.1 PriorityQueue实现类
- 从PriorityQueue中取出元素时,不按插入顺序,而是按从小到大顺序
- PriorityQueue对象的toString方法不一定按从小到大顺序打印
- toString:[元素1,元素2,…]
- 有自然排序和定制排序,与TreeSet细节相同
8.5.2 Deque接口与ArrayDeque实现类
- Deque为Queue子接口,代表一个双端队列,即允许元素从头或尾进行插入和读取
- Deque定义了一些双端队列的方法,允许两端来操作队列中的元素
boolean offerFirst(Object e)
boolean offerLast(Object e)
//插入元素到队列头,越界报错
void addFirst(Object e)
//插入元素到队尾,越界报错
void addLast(Object e)
//为入栈(头进头出)方法,同addFirst
void push(Object e)
//获取队列头元素,并删除,队列中无元素返回null
Object pollFirst()
//获取队尾元素,并删除,队列中无元素返回null
Object pollLast()
//获取队列头元素,并删除,队列中无元素报错
Object removeFirst()
//获取队尾元素,并删除,队列中无元素报错
Object removeLast()
//为出栈(头进头出)方法,同removeFirst
Object pop()
//获取队列头元素,不删除,队列中无元素返回null
Object peekFirst()
//获取队列尾元素,不删除,队列中无元素返回null
Object peekLast()
//获取队列头元素,不删除,队列中无元素报错
Object getFirst()
//获取队尾元素,不删除,队列中无元素报错
Object getLast()
//从队列头开始遍历,删除第一次出现的元素o Occurrence:[əˈkʌrəns]发生
boolean removeFirstOccurrence(Object o)
//从队尾开始遍历,删除第一次出现的元素o
boolean removeLastOccurrence(Object o)
//返回该双端队列对应的迭代器,该迭代器以逆向顺序迭代队列中的元素 descending:下降的
Iterator descendingIterator()
- ArrayDeque为Deque实现类,是基于数组实现的,可以指定numElements参数来指定队列容量,默认16
- ArrayDeque由于支持头进头出,所以可以模拟栈的行为,因此一般用ArrayDeque来代替Vector的Stack子类
8.5.3 LinkedList实现类
LinkedList实现List接口,所以可以根据索引随机访问集合中的元素,同时也实现了Deque接口,所以也可以被当做双端队列使用
8.5.4 各种线性表的性能分析
- 线性表分类
- 双向链表:开辟、销毁内存空间的次数相对较多,但不会造成内存空间的浪费
- 动态数组:开辟、销毁内存空间的次数相对较少,但可能造成内存空间浪费(可以通过缩容解决)
- ArrayList是通过动态数组实现的,LinkedList是通过双向链表实现的
- 对于插入和删除
- 动态数组:
- 当容量不够时,需要进行数组复制操作进行扩容
- 在指定位置插入、删除元素时,通过复制该元素后的数组来完成
- 双向链表:
- 指定位置插入、删除时,只需找到该元素,并维护相应的Entry中标记前一个元素和后一个元素所对应的指针即可
- 动态数组:
- 几种情况下线性表选择
- 如果频繁在尾部进行添加、删除操作,动态数组、双向链表均可选择
- 如果频繁在头部进行添加、删除操作,建议选择使用双向链表
- 如果有频繁的(在任意位置)添加、删除操作,建议选择使用双向链表
- 如果有频繁的查询操作(随机访问操作),建议选择使用动态数组
- 多线程访问集合中元素,应用Collections包装集合
8.6 Map
8.6.1 Map理解与基本方法
- Map中保存两组值,一组值可以组成Set集合,用于保存Map中的key,另一组值可以组成Collection,用于保存Map中的value,key和value都可以是任何数据类型
- Java先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合,因此所有key放在一起就组成了一个Set集合,Map的key不允许重复,因此Set集合不允许插入重复数据
- Map接口方法
//添加一个key-value对,如果原来已有该key,则新value覆盖原value
Object put(Object key,Object value)
//将m中所有key-value对放入集合中
void putAll(Map m)
//删除key对应的key-value对,返回value值,如果key不存在,返回null
Object remove(Object key)
//删除所有key-value对
void clear()
//返回key所对应的value,都不包含此key,返回null
Object get(Object key)
//是否包含指定的key
boolean containsKey(Object key)
//是否包含指定的value
boolean containsValue(Object value)
//返回key-value对个数
int size()
//Map中是否没有元素(不包含任何key-value对)
boolean isEmpty()
//返回Map中key-value对所组成的Set集合,因为Map中key不允许重复,所以一定可以组成一个Set集合,每个元素都是Map的内部类Entry的对象
Set entrySet()
//返回所有key组成的Set集合
Set keySet()
//返回所有values组成的Collection
Collection values()
- Map.Entry的方法:entrySet方法可以获得Set<Map.Entry>,通过Iterator进行遍历即可得到每个Map.Entry对象
//返回Entry中的key值
Object getKey()
//返回Entry中的value值
Object getValue()
//设置Entry中的value值,并返回新value值
Object setValue(V value)
- for-each遍历Map集合
Map map = new HashMap();
for(Object key : map.keySet()){
System.out.println(key+"-->"+map.get(key));
}
- Map的toString()方法
{key1==value1,key2==value2,...}
8.6.2 HashMap与Hashtable实现类
- HashMap线程不安全,Hashtable线程安全但性能低,Hashtable拥有方法elements(类似于Map中的values()),keys(类似于keySet()),Hashtable不允许null作为key(会调用key的hashCode方法)和value(写死的,如果为空就抛异常,怀疑和多线程有关),HashMap就可以
- HashMap/Hashtable判断key相等的要求与HashSet相同,判断value相等的要求与Collection相同,equals方法比较返回true。即对于方法containsValue(),两个对象equals方法相同,即返回true
8.6.3 LinkedHashMap实现类
与LinkedHashSet相同
8.6.4 Properties实现类:用于读写配置文件
- Properties为Hashtable子类,该类可把Map对象和属性文件关联起来,从而将Map中的key-value对写入属性文件,也可以将属性文件中"属性名=属性值"加载到Map对象中,注意key与value都应该是String类型
- Properties提供的方法
//从文件(以输入流表示)加载key-value对,追加到Properties对象中,无法保证顺序,文件格式必须为key=value
void load(InputStream inStream)
//相当于返回String的Map的get方法
String getProperty(String key)
//如果不存在指定key,返回default
String getProperty(String key,String default)
//相当于Map的put方法,不会改变文件本身
Object setProperty(String key,String value)
//将comments内容输出到文件中,前面加#
void store(OutputStream out ,String comments)
- Properties可以将key-value对以XML文件格式保存,也可以从XML中加载key-value
8.6.5 SortedMap接口和TreeMap实现类
- SortedMap对key的要求与TreeSet对元素的要求完全相同
- TreeMap方法
//返回最小key所对应的key-value对,如果Map为空,返回null
Map.Entry firstEntry()
//返回最大key所对应的key-value对,如果Map为空,返回null
Map.Entry lastEntry()
//返回最小key,如果Map为空,返回null
Object firstKey()
//返回最大key,如果Map为空,返回null
Object lastKey()
//返回最大于key的最小key所对应的key-value对
Map.Entry higherEntry(Object key)
Object higherKey(Object key)
Map.Entry lowerEntry(Object key)
Object lowerKey(Object key)
//返回Map的子Map,是否包含由第二和第四个元素决定
SortedMap subMap(Object fromKey,Object toKey)
//fromInclusive/toInclusive决定返回的Map对象,是否包含fromKey与toKey对应的Entry
navigableMap subMap(Object fromKey,boolean fromInclusive,Object toKey,boolean toInclusive)
SortedMap tailMap(Object fromKey)
navigableMap tailMap(Object fromKey,boolean inclusize)
SortedMap headMap(Object toKey)
navigableMap headMap(Object toKey,boolean inclusize)
8.6.6 WeakHashMap实现类
- 与HashMap用法基本相似,一般用于缓存的实现
- 弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收
- HashMap的key保留对实际对象的强引用,所以只要HashMap对象不被销毁,key对象永远不会被垃圾回收,所以key-value也不会被垃圾回收,而WeakHashMap只保留key对象弱引用,如果key对象没被其他强引用对象所引用,则这些key会被回收,其对应的key-value对也会被回收
- 更直观的说,当使用 WeakHashMap 时,即使没有显示的添加或删除任何元素,也可能发生如下情
- 调用两次size()方法返回不同的值
- 两次调用isEmpty()方法,第一次返回false,第二次返回true
- 两次调用containsKey()方法,第一次返回true,第二次返回false,尽管两次使用的是同一个key
- 两次调用get()方法,第一次返回一个value,第二次返回null,尽管两次使用的是同一个对象
- 例:key为new String(“语文”),是匿名字符串对象,无强引用引用他,所以会被回收,key为"语文"是字符串直接量,系统会自动保存对该字符串对象的强引用,所以不会被回收
- 不应该让key对象具有任何强引用,否则会失去WeakHashMap的意义
8.6.7 IdentityHashMap实现类
Identity意思为"身份",IdentityHashMap判断key值相等的条件与HashMap不同
- HashMap:equals返回true、hashCode值相同
- IdentityHashMap:==返回true
8.6.8 EnumMap实现类:
- 对key的要求与EnumSet对元素的要求一致
- EnumMap的key,必须为构造方法所传入的枚举类的枚举值
//key必须为枚举类Season的枚举值中的一个
EnumMap a = new EnumMap(Season.class)
8.6.9 Map实现类的性能分析:
与Set相同,一般使用HashMap
8.7 操作集合的工具类:Collections
8.7.1 排序操作
//自然顺序对list升序排序
static void sort(List list)
//根据c对list升序排序
static void sort(List list,Comparator c)
//反转list中的元素顺序
static void reverse(List list)
//shuffle:洗牌 list中元素随机排序
static void shuffle(List list)
//swap:交换 交换i,j处元素
static void swap(List list,int i,int j)
//rotate:旋转 如果distance为正,将后distance个元素放在list开头,如果distance为负,将前distance元素放在list尾巴
static void rotate(List list,int distance)
8.7.2 查找与替换
//以二分法搜索list,返回元素o的索引,List中值必须处于有序状态,否则返回值不正确,元素多时,比indexOf效率高
static int binarySearch(List list,Object o)
//返回o出现次数
static int frequency(Collection c,Object o)
//返回target集合在source中第一次出现的位置索引,如果没有出现过,返回-1
static int indexOfSubList(List source,List target)
static int lastIndexOfSublist(List source,List target)
//根据元素自然排序,返回最大元素
static Object max(Collection coll)
static Object max(Collection coll,Comparator comp)
//根据comp排序,返回最小元素
static Object min(Collection coll,Comparator comp)
//用obj替换list中所有元素
static void fill(List list,Object obj)
//使用newVal替换list中所有oldVal元素
static boolean replaceAll(List list,Object oldVal,Object newVal)
8.7.3 获取线程安全的集合
//实现原理:
//1. 将集合转为了一个SynchronizedCollection对象,该对象为Collections工具类中的一个静态内部类,该内部类中成员变量c保存传入的集合,成员变量mutex保存自身,且这两个成员变量为final定义,不允许指向别处
//2. 当对该对象做add操作时,synchronized (mutex),即同一时间,只有一个线程可以对该集合进行add操作
//Xxxx一般为Collection、Set、List、Map
Xxxx c = Collections.synchronizedXxxx(Xxxx o);
8.7.4 获取不可变的集合
即只能访问集合元素,但不能修改集合元素
//返回空的不可改变的集合对象,返回值可以为List,Set,SortedSet,Map,SortedMap等等
static Xxxx emptyXxxx()
//返回只包含元素o的不可变的集合
static Xxxx singletonXxxx(Object o)
//返回包含o集合中所有元素的不可变的集合
static Xxxx unmodifiableXxxx(Collection/Map o)
8.8 Enumeration迭代器
- Enumeration作用与Iterator相同
- Enumeration几乎只能迭代Vector,Hashtable这种古老的集合
- Vector有使用elements方法获、Hashtable使用elements和keys方法,取该迭代器
- Enumeration中方法
boolean hasMoreElements():
Object nextElement():