0、集合引入
1)集合的由来?
Java是面向对象编程语言,经常需要操作很多对象,必要时需存储对象(对Java语言而言,存储的通常是对象的引用,以达到复用或管理等目的),常见容器如数组和StringBuffer(线程安全但效率较低,为了提高效率而引进StringBuilder)。
通常数组的长度固定,所以不适合做变化的需求(可以手动去重新分配调整,较麻烦),而StringBuffer(或StringBuilder)存储元素为字符串,皆不符合存储任意元素的要求。因此集合就应运而生了。
2)集合和数组的区别?
1 长度限制之别
- 数组长度是固定不变的
- 集合的大小是可动态变化的
2 存储类型之别
- 一个数组存储的元素可以是基本类型,也可以是引用类型,且只能存储同一种类型的元素
- 一个集合存储的元素只能是引用类型,但集合可以存储不同类型的元素(但集合一般存储同一种类型,可以用泛型加以控制)
3 访问元素方式
- 数组是根据索引来获取元素的
- 集合通常会提供一个迭代器来方便访问元素
3)java集合框架初窥
- 从图上我们可以清晰的看到Java集合分为两大类:
- 以Collection接口为规范衍生的单列集合,存储单列数据或对象
- 以Map接口为规范衍生的双列集合,存储key-value映射或关联关系
- iterator 是迭代器,用于遍历集合元素
本文着重来写Collection集合。Map集合介绍见Java集合框架之Map实例解析
注:图片来源于网络。类已经很全了,就不赘画了。
1、Collection之继承体系
Collection<E>
接口是所有单列集合的共同父接口,下面列出了常用的Collection子类集合及其继承关系。
Collection
|-----List 有序(存储顺序和取出顺序一致),可重复
|----ArrayList ,线程不安全,底层使用数组实现,查询快,增删慢。效率高。
每次容量不足时,自增长度的一半,如下源码可知
int newCapacity = oldCapacity + (oldCapacity >> 1);
|----LinkedList , 线程不安全,底层使用链表实现,查询慢,增删快。效率高
|----Vector , 线程安全,底层使用数组实现,查询快,增删慢。效率低
每次容量不足时,默认自增长度的一倍(如果不指定增量的话),如下源码可知
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
|-----Set 元素唯一
一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。
|--HashSet 底层是由HashMap实现的,通过对象的hashCode方法与equals方法来保证插入元素的唯一性,无序(存储顺序和取出顺序不一致)。
|--LinkedHashSet 底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序。(存储和取出是一致)
|--TreeSet 基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。 元素唯一。
2、Collection泛型接口
方法摘要
boolean add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void clear()
移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean equals(Object o)
比较此 collection 与指定对象是否相等。
int hashCode()
返回此 collection 的哈希码值。
boolean isEmpty()
如果此 collection 不包含元素,则返回 true。
Iterator<E> iterator()
返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int size()
返回此 collection 中的元素数。
Object[] toArray()
返回包含此 collection 中所有元素的数组。
<T> T[]
toArray(T[] a)
使用Collection示例
public class CollectionReview {
public static void main(String[] args) {
test1();
}
private static void test1() {
// Collection<E>为接口,使用多态进行创建
Collection<String> collection = new Vector<>();
collection.add("gogogo");
collection.add("pap");
collection.add("niko");
collection.add("kitty");
Collection<String> coll = new ArrayList<>();
coll.add("niko");
coll.add("kitty");
coll.add("pecuyu");
// collection.clear(); // 清空集合
//System.out.println(collection.isEmpty()); // 集合是否为空
// int size = collection.size(); // 获取集合大小
// System.out.println(size);
// boolean contains = collection.contains("niko"); // 是否包含另一个元素
// System.out.println(contains);
//boolean containsAll = collection.containsAll(coll); //是否完全包含另一个集合
//System.out.println(containsAll);
// collection.remove("kitty"); // 删除第一个匹配项,删除了匹配项则返回true
// boolean removeAll = collection.removeAll(coll); // 删除与指定集合有交集的部分,原集合有改变就返回true
// System.out.println(removeAll);
//boolean retainAll = collection.retainAll(coll);// 保留与指定集合有交集的部分,原集合有改变就返回true
//System.out.println(retainAll);
// iterator 迭代器, 方式1
Iterator<String> iterator = collection.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next()+" ");
}
System.out.println("\n"+"-------------------");
// 方式2 ,for循环完iterator1就会被销毁,节约内存提高效率
for (Iterator<String> iterator1 = collection.iterator(); iterator1.hasNext(); ) {
System.out.print(iterator1.next()+" ");
}
System.out.println("\n"+"-------------------");
Object[] array = collection.toArray(); // 转化为object数组
for (Object string : array) {
System.out.print(string+" ");
}
System.out.println("\n"+"-------------------");
String[] arr=new String[collection.size()];
String[] array2 = collection.toArray(arr); // 指定要转化的数组类型
for (String string : array2) {
System.out.print(string+" ");
}
}
3、List泛型接口
继承自Collection
特有方法(相对于Collection)
void add(int index, E element)
在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
E get(int index)
返回列表中指定位置的元素。
int indexOf(Object o)
返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o)
返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
ListIterator<E> listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
ListIterator<E> listIterator(int index)
返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
E remove(int index)
移除列表中指定位置的元素(可选操作)。
E set(int index, E element)
用指定元素替换列表中指定位置的元素(可选操作)。
List<E> subList(int fromIndex, int toIndex)
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
3.1、ArrayList类
继承自AbstractList,实现了Iterable, Collection, List,Cloneable, Serializable
每次容量不足时,自增长度的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
1 构造方法
ArrayList()
构造一个初始容量为 10 的空列表(每次递增容量的一半)
ArrayList(Collection<? extends E> c)
构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的
ArrayList(int initialCapacity)
构造一个具有指定初始容量的空列表
2 特有方法(相对于List)
void ensureCapacity(int minCapacity)
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
3 ArrayList使用示例
public class ArrayListReview {
public static void main(String[] args) {
ArrayList<Info> infosList=new ArrayList<>();
infosList.add(new Info(0, "aaa"));
infosList.add(new Info(1, "bbb"));
infosList.add(new Info(2, "ccc"));
infosList.add(new Info(3, "ddd"));
infosList.add(1, new Info(4, "eee")); // 按索引插入到指定位置
// foreach遍历输出
for (Info info : infosList) {
System.out.println(info);
}
System.out.println("------------------------");
// 截取子List
List<Info> subList = infosList.subList(1, 3);
subList.add(new Info(30, "fly"));
printList(subList);
ArrayList<Info> newInfosList=new ArrayList<>();
newInfosList.add(new Info(11, "qqq"));
newInfosList.add(new Info(12, "www"));
ArrayList<String> ss=new ArrayList<>();
// infosList.addAll(newInfosList); // 添加一个指定集合到原集合最后,注意两个集合的泛型参数一致
infosList.addAll(2,newInfosList); // 将指定集合插入到指定位置
//printList(infosList);
Info info = infosList.get(2); // 取出指定位置的元素
System.out.println(info);
infosList.set(0, new Info(10, "rrr")); // 替换指定索引位置的元素
//printList(infosList);
int index = infosList.indexOf(info); //根据元素获取元素第一次出现的索引,不存在则返回-1
int lastIndex = infosList.lastIndexOf(info); // 取出元素的最后一个匹配项的索引
int indexOf = infosList.indexOf(new Info(4,"eee")); // 重写了Info类的hashCode与equals方法,用于判断两个对象是否相同
System.out.println("index="+indexOf);
//printList(infosList);
// 通过反射拿到的removeRange方法
removeRange(infosList, 1, 3);
//printList(infosList);
// listIterator从前往后迭代
ListIterator<Info> listIterator = infosList.listIterator();
while(listIterator.hasNext()){
// 最后抛出错误,java.util.NoSuchElementException,不要每次取都掉用next方法,它每调用一次,迭代器指针向前移动一位
// System.out.println("id="+listIterator.next().getId()
// +"adress="+listIterator.next().getAdress());
Info next = listIterator.next(); // 正确做法,调用一次要取出元素,然后操作属性
System.out.println("id="+next.getId()
+" adress="+next.getAdress());
}
// 往前迭代,必须在往后迭代之后用
while(listIterator.hasPrevious()){ // 当有上一个元素时
Info previous = listIterator.previous(); // 获取上一个元素
System.out.println("id="+previous.getId()+" adresss="+previous.getAdress());
}
// 通过数组来转化成一个List,虽然可以把数组转成集合,但是集合的长度不能改变。
String[] a=new String[]{"hello","world","just","do","it"};
List<String> asList = Arrays.asList(a);
//asList.add("gogogo"); //不可修改asList,会抛出UnsupportedOperationException
// 泛型使用
// ArrayList<Object> l=new ArrayList<String>();// 不行
// ArrayList<ArrayList> lll=new ArrayList<List>(); // 不行
// ArrayList<List> lll=new ArrayList<ArrayList>(); // 不行
// ArrayList l=new ArrayList<String>(); // 可行
// ArrayList<String> l=new ArrayList<>(); // 可行
// ArrayList ll=new ArrayList(); // 可行
}
// 打印输出
public static <E> void printList(List<E> list) {
for (E e : list) {
System.out.println(e);
}
System.out.println("------------------------");
}
}
4 ArrayList的removeRange的使用
removeRange方法是protect访问权限,无法直接使用,可以通过反射拿到它(反射相关不熟悉的可以参考 Java反射机制)
/**
* 通过反射调用ArrayList的removeRange方法
* @param list 用于删除元素的list
* @param fromIndex 起始删除索引,包含
* @param toIndex 结束删除索引,不包含
*/
public static <E> void removeRange(ArrayList<E> list,int fromIndex, int toIndex) {
try {
// 拿到ArrayList的Class对象
Class<?> clazz = Class.forName("java.util.ArrayList");
// 通过clazz对象拿到removeRange方法,参数为(int,int)
Method declaredMethod = clazz.getDeclaredMethod("removeRange", int.class,int.class);
// 将方法的访问权限设置为可访问
declaredMethod.setAccessible(true);
// 调用removeRange方法
declaredMethod.invoke(list,fromIndex,toIndex);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
Info类如下
public class Info {
private int id;
private String adress;
public Info(int id, String adress) {
super();
this.id = id;
this.adress = adress;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
@Override
public String toString() {
return "Info [id=" + id + ", adress=" + adress + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((adress == null) ? 0 : adress.hashCode());
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Info other = (Info) obj;
if (adress == null) {
if (other.adress != null)
return false;
} else if (!adress.equals(other.adress))
return false;
if (id != other.id)
return false;
return true;
}
}
3.2、LinkedList类
继承自AbstractSequentialList,实现了List, Deque, Cloneable, Serializable
底层使用链表实现,查询慢,增删快。线程不安全,效率高
1 构造方法
LinkedList()
构造一个空列表。
LinkedList(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。
2 特有方法(相对于List)
实现了Deque, Queue ,获得了他们的方法,关系如下
Queue 方法 | 等效 Deque 方法 |
---|---|
add(e) | addLast(e) |
offer(e) | offerLast(e) |
remove() | removeFirst() |
poll() | pollFirst() |
element() | getFirst() |
peek() | peekFirst() |
堆栈方法(Deque作堆栈) | 等效 Deque 方法 |
---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
void addFirst(E e)
将指定元素插入此列表的开头。
void addLast(E e)
将指定元素添加到此列表的结尾。
Iterator<E> descendingIterator()
返回以逆向顺序在此双端队列的元素上进行迭代的迭代器。
E element()
获取但不移除此列表的头(第一个元素)。
E getFirst()
返回此列表的第一个元素。
E getLast()
返回此列表的最后一个元素。
boolean offer(E e)
将指定元素添加到此列表的末尾(最后一个元素)。
boolean offerFirst(E e)
在此列表的开头插入指定的元素。
boolean offerLast(E e)
在此列表末尾插入指定的元素。
E peek()
获取但不移除此列表的头(第一个元素)。
E peekFirst()
获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
E peekLast()
获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
E poll()
获取并移除此列表的头(第一个元素)
E pollFirst()
获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
E pollLast()
获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
E pop()
从此列表所表示的堆栈处弹出一个元素。
void push(E e)
将元素推入此列表所表示的堆栈。
E removeFirst()
移除并返回此列表的第一个元素。
E removeLast()
移除并返回此列表的最后一个元素。
boolean removeFirstOccurrence(Object o)
从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。
boolean removeLastOccurrence(Object o)
从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。
3 LinkedList使用示例
public class LinkListReview {
public static void main(String[] args) {
LinkedList<String> list=new LinkedList<>();
list.addFirst("qqq"); // 将指定元素插入此列表的开头。
list.addLast("eee"); // 将指定元素添加到此列表的结尾。
list.addFirst("www"); // 到此list的排序为: www qqq eee
// list.set(2, "ttt"); // 替换指定位置的元素,注意索引要小于list的size
Iterator<String> descendingIterator = list.descendingIterator(); // 反向迭代器
while (descendingIterator.hasNext()) {
System.out.print(descendingIterator.next()+" ");
}
System.out.println("\n"+"-------------------------");
// ListIterator迭代,指定迭代的起始位置
ListIterator<String> listIterator = list.listIterator(1); // 正反向都可迭代,反向迭代须在正向之后进行,
while(listIterator.hasNext()){
System.out.print(listIterator.next()+" ");
}
System.out.println("\n"+"-------------------------");
// 元素的获取与添加
String element = list.element(); // 获取但不移除此列表的头(第一个元素)
System.out.println(element);
String first = list.getFirst(); // 返回此列表的第一个元素
String last = list.getLast(); // 返回此列表的最后一个元素
System.out.println("first="+first+" last="+last);
System.out.println("peek="+list.peek());// peek() 获取但不移除此列表的头(第一个元素)。
System.out.println("peekFirst="+list.peekFirst()); //E peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
System.out.println("peekLast="+list.peekLast()); E peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
printList(list);
System.out.println("poll="+list.poll()); // poll() 获取并移除此列表的头(第一个元素)
System.out.println("pollFirst="+list.pollFirst());//E pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
System.out.println("pollLast="+list.pollLast());//E pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
printList(list);
list.push("push"); //void push(E e) 将元素推入此列表所表示的堆栈,相当于addFirst(E e)。此方法继承自Deque
list.push("push1");
list.push("push2");
list.pop(); //E pop() 从此列表所表示的堆栈处弹出一个元素。 若list的集合为空,则抛出//E pop() 从此列表所表示的堆栈处弹出一个元素。
printList(list);
list.offer("offer"); // 将指定元素添加到此列表的末尾(最后一个元素)。
list.offerFirst("offerFirst"); //在此列表的开头插入指定的元素。
list.offerLast("offerLast"); //在此列表末尾插入指定的元素。
printList(list);
// removeFirstOccurrence删除第一个匹配项,removeLastOccurrence删除最后一个匹配项 都是顺序遍历方向
System.out.println("removeFirstOccurrence="+list.removeFirstOccurrence("offerFirst"));
System.out.println("removeLastOccurrence="+list.removeLastOccurrence("offerLast"));
printList(list);
// 通过集合的方式山删除要删除的元素
LinkedList<String> dyingList=new LinkedList<>();
for(ListIterator<String> li=list.listIterator();li.hasNext();){
String next = li.next();
if (next.equals("push")) {
// list.removeFirstOccurrence(next); // 不能再循环时修改,抛出并发修改异常ConcurrentModificationException
dyingList.add(next); // 将要删除的元素存储起来以备删除
}
}
list.removeAll(dyingList); // 删除要移除的元素
printList(list);
// ListIterator进行遍历,并进行修改
for(ListIterator<String> it=list.listIterator();it.hasNext();){
String next=it.next();
if(next.equals("offer")){
// it.remove(); // 移除当前,即next所指元素
// it.set("newOffer"); // 替换当前,即next所指元素
it.add("newHere"); // 添加元素,在next所指元素之后
}
}
printList(list);
// for循环通过list进行修改
for(int i=0;i<list.size();i++){
if(list.get(i).equals("push1")){
list.remove(i);
}
}
printList(list);
}
public static <E> void printList(List<E> list) {
for (E e : list) {
System.out.print(e+" ");
}
System.out.println("\n-------------------------");
}
}
3.3、Vector类
继承自AbstractList,实现了 List,Cloneable, Serializable接口
每次容量不足时,默认自增长度的一倍(如果不指定增量的话)
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
3.31 构造方法摘要
Vector()
构造一个空向量,使其内部数据数组的大小为 10,其标准容量增量为零。
Vector(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的向量,这些元素按其 collection 的迭代器返回元素的顺序排列。
Vector(int initialCapacity)
使用指定的初始容量和等于零的容量增量构造一个空向量。
Vector(int initialCapacity, int capacityIncrement)
使用指定的初始容量和容量增量构造一个空的向量。
3.32 特有方法摘要
void addElement(E obj)
将指定的组件添加到此向量的末尾,将其大小增加 1。
void copyInto(Object[] anArray)
将此向量的组件复制到指定的数组中。
E elementAt(int index)
返回指定索引处的组件。
Enumeration<E> elements()
返回此向量的组件的枚举。
void ensureCapacity(int minCapacity)
增加此向量的容量(如有必要),以确保其至少能够保存最小容量参数指定的组件数。
E firstElement()
返回此向量的第一个组件(位于索引 0) 处的项)。
void insertElementAt(E obj, int index)
将指定对象作为此向量中的组件插入到指定的 index 处。
E lastElement()
返回此向量的最后一个组件。
void removeAllElements()
从此向量中移除全部组件,并将其大小设置为零。
boolean removeElement(Object obj)
从此向量中移除变量的第一个(索引最小的)匹配项。
void removeElementAt(int index)
删除指定索引处的组件。
protected void removeRange(int fromIndex, int toIndex)
从此 List 中移除其索引位于 fromIndex(包括)与 toIndex(不包括)之间的所有元素。
void setElementAt(E obj, int index)
将此向量指定 index 处的组件设置为指定的对象。
void setSize(int newSize)
设置此向量的大小。
3.33 Vector使用示例
public class VectorReview {
public static void main(String[] args) {
Vector<String> vector=new Vector<>();
vector.addElement("e1");
vector.addElement("e2");
vector.addElement("e3");
vector.addElement("e4");
vector.addElement("e5");
vector.add(3, "e6"); //在指定位置添加元素,非覆盖
printList(vector);
// copyInto将内容拷贝到一个数组中
String[] anArray=new String[vector.size()]; // 注意数组长度不要小于vector的size
vector.copyInto(anArray);
for (String string : anArray) {
System.out.print(string+" ");
}
System.out.println("\n--------");
// 返回指定位置的元素
String elementAt = vector.elementAt(2);
System.out.println(elementAt);
System.out.println(vector.firstElement()); // 返回此向量的第一个组件,索引0的元素
System.out.println(vector.lastElement()); //返回此向量的最后一个组件
// 删除元素
//vector.removeAllElements(); // 从此向量中移除全部组件,并将其大小设置为零。
System.out.println(vector.removeElement("e5")); // 从此向量中移除变量的第一个(索引最小的)匹配项。
vector.removeElementAt(2); // 删除指定索引处的组件。
vector.setElementAt("yyy", 3); // 将此向量指定 index 处的组件设置为指定的对象。注意index要小于vector的size
printList(vector);
// 返回此向量的组件的枚举
Enumeration<String> elements = vector.elements();
while(elements.hasMoreElements()){
System.out.print(elements.nextElement()+" ");
}
System.out.println("\n--------");
}
public static void printList(List<String> list) {
for (String string : list) {
System.out.print(string+" ");
}
System.out.println("\n-----------------");
}
3.4 并发修改异常
1:出现的现象
迭代器遍历集合,集合修改集合元素
2:原因
迭代器是依赖于集合的,而集合的改变迭代器并不知道。
3:解决方案
1:迭代器遍历,迭代器修改(ListIterator)
元素添加在刚才迭代的位置
2:集合遍历(普通for循环),集合修改
元素添加在集合的末尾
3:遍历集合,将要删除的元素添加到另一个集合,然后原集合通过removeAll删除待删除的集合
方案3示例
// 通过集合的方式山删除要删除的元素
LinkedList<String> dyingList=new LinkedList<>();
for(ListIterator<String> li=list.listIterator();li.hasNext();){
String next = li.next();
if (next.equals("push")) {
// list.removeFirstOccurrence(next); // 不能再循环时修改,抛出并发修改异常ConcurrentModificationException
dyingList.add(next); // 将要删除的元素存储起来以备删除
}
}
list.removeAll(dyingList); // 删除要移除的元素
3.5 Iterator的替代品 -增强for循环
(1)for循环的一种,另一种书写形式
(2)书写格式:
for(元素的数据类型 变量名 : 数组或者Collection集合的对象) {
使用该变量即可,该变量其实就是数组或者集合中的元素。
}
(3)好处:
简化了数组和集合的遍历
(4)弊端
增强for循环的目标不能为null。建议在使用前,先判断是否为null。并且,也没有元素的index位置
// 增强for
for (String s : array) {
System.out.println(s);
}
反编译可知增强是通过Iterator来实现的
for (Iterator iterator = array.iterator(); iterator.hasNext(); System.out.println(s))
s = (String)iterator.next();
4. Set泛型接口
一个不包含重复元素的 collection,实现了Collection, Iterable 接口,常见子类 HashSet, LinkedHashSet, TreeSet
方法摘要
相对于Collection没有特殊方法,略
4.1 TreeSet
TreeSet 基于 TreeMap 的 NavigableSet 实现。非同步,排序,元素唯一。通过Collections.synchronizedSortedSet 方法来“包装”该 TreeSet实现同步
必须保证元素可排序性,如下两种方法
① 使用元素的自然顺序对元素进行排序(元素要实现Comparable接口并重写compare方法)。
② 根据创建 set 时提供的 Comparator 进行排序。
4.11 构造方法摘要
TreeSet()
构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet(Collection<? extends E> c)
构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。
TreeSet(SortedSet<E> s)
构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。
4.12 特有方法摘要 (相对于Set)
E ceiling(E e)
返回此 set 中大于等于给定元素的最小元素;如果不存在这样的元素,则返回 null。
Comparator<? super E> comparator()
返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。
Iterator<E> descendingIterator()
返回在此 set 元素上按降序进行迭代的迭代器。
NavigableSet<E> descendingSet()
返回此 set 中所包含元素的逆序视图。
E first()
返回此 set 中当前第一个(最低)元素。
E floor(E e)
返回此 set 中小于等于给定元素的最大元素;如果不存在这样的元素,则返回 null。
SortedSet<E> headSet(E toElement)
返回此 set 的部分视图,其元素严格小于 toElement。
NavigableSet<E> headSet(E toElement, boolean inclusive)
返回此 set 的部分视图,其元素小于(或等于,如果 inclusive 为 true)toElement。
E higher(E e)
返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 null。
Iterator<E> iterator()
返回在此 set 中的元素上按升序进行迭代的迭代器。
E last()
返回此 set 中当前最后一个(最高)元素。
E lower(E e)
返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 null。
E pollFirst()
获取并移除第一个(最低)元素;如果此 set 为空,则返回 null。
E pollLast()
获取并移除最后一个(最高)元素;如果此 set 为空,则返回 null。
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
返回此 set 的部分视图,其元素范围从 fromElement 到 toElement。
SortedSet<E> subSet(E fromElement, E toElement)
返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。
SortedSet<E> tailSet(E fromElement)
返回此 set 的部分视图,其元素大于等于 fromElement。
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
返回此 set 的部分视图,其元素大于(或等于,如果 inclusive 为 true)fromElement。
4.13 TreeSet使用示例
public class TreeSetReview {
public static void main(String[] args) {
// test1();
test2();
}
/**
* 自然排序
*/
private static void test1() {
TreeSet<String> treeSet=new TreeSet<String>();
treeSet.add("a");
treeSet.add("d");
treeSet.add("b");
treeSet.add("e");
treeSet.add("c");
for (String string : treeSet) {
System.out.println(string);
}
}
/**
*
* TreeSet添加自定义对象时满足其一 ①构造时传入比较器 ②实现Comparable接口并重写compare方法,否则抛出异常
* java.lang.ClassCastException:
* com.yu.bean.Info cannot be cast to java.lang.Comparable
*/
private static void test2() {
TreeSet<Info> infosSet =new TreeSet<Info>(new Comparator<Info>() { // 传入比较器,保证顺序
@Override
public int compare(Info o1, Info o2) {
int num=o1.getId()-o2.getId();
num = num==0?o1.hashCode()-o2.hashCode():num;
return num;
}
}) ;
infosSet.add(new Info(0, "qqq"));
infosSet.add(new Info(1, "wew"));
infosSet.add(new Info(3, "ete"));
infosSet.add(new Info(2, "bjk"));
infosSet.add(new Info(1, "wew")); // 重复元素,compare方法返回0,不会被插入到TreeSet
for (Info info : infosSet) {
System.out.println(info);
}
// output:
// Info [id=0, adress=qqq]
// Info [id=1, adress=wew]
// Info [id=2, adress=bjk]
// Info [id=3, adress=ete]
// 可以看出,Comparator保证了元素的有序性,重复元素将不会被添加到TreeSet
}
}
4.2 LinkedHashSet
LinkedHashSet,具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现 .不是同步的。使用 Collections.synchronizedSet 方法来“包装”该 set完成同步。
底层数据结构由哈希表和链表组成。
- 哈希表保证元素的唯一性。
- 链表保证元素有序。(存储和取出是一致)
如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)
void foo(Set s) {
Set copy = new LinkedHashSet(s);
...
}
4.21 构造方法摘要
LinkedHashSet()
构造一个带默认初始容量 (16) 和加载因子 (0.75) 的新空链接哈希 set。
LinkedHashSet(Collection<? extends E> c)
构造一个与指定 collection 中的元素相同的新链接哈希 set。
LinkedHashSet(int initialCapacity)
构造一个带指定初始容量和默认加载因子 (0.75) 的新空链接哈希 set。
LinkedHashSet(int initialCapacity, float loadFactor)
构造一个带有指定初始容量和加载因子的新空链接哈希 set。
4.22 LinkedHashSet使用示例
public class LinkedHashSetReceive {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet=new LinkedHashSet<String>();
linkedHashSet.add("a");
linkedHashSet.add("d");
linkedHashSet.add("b");
linkedHashSet.add("f");
linkedHashSet.add("c");
for (String string : linkedHashSet) {
System.out.println(string);
}
}
}
4.3 HashSet
HashSet 底层由HashMap实现,通过对象的hashCode方法与equals方法来保证插入元素的唯一性,它不保证 set 的迭代顺序。元素存储无序,且唯一。使用 Collections.synchronizedSet 方法来“包装” HashSet实现同步
4.31 构造方法摘要
HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的新 set。
HashSet(int initialCapacity)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
4.32 HashSet使用示例
public class HashSetReview {
public static void main(String[] args) {
test1();
test2();
test3();
}
/**
* 当添加的是String或基本数据类型(自动装箱)
*/
private static void test1() {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("你好啊");
hashSet.add("很好");
hashSet.add("很不错");
hashSet.add("你好啊"); // 重复元素将不会被添加到set
for (String string : hashSet) {
System.out.println(string);
}
HashSet<Integer> integers=new HashSet<Integer>();
integers.add(21);
integers.add(26);
integers.add(27);
integers.add(32);
integers.add(29);
integers.add(21);
for (Integer integer : integers) {
System.out.println(integer);
}
// output:
// 很不错
// 你好啊
// 很好
// 32
// 21
// 26
// 27
// 29
// 可见添加String类型或基本类型包装类时(他们重写了equals和hashCode方法),重复元素不会添加到集合中,且输出与添加时的顺序无关
}
/**
* 测试添加自定义对象 - 没有重写了equals和hashCode方法时(去Info类中注释掉重写的方法)
*/
private static void test2() {
HashSet<Info> infoHashSet=new HashSet<Info>();
infoHashSet.add(new Info(0, "qqq"));
infoHashSet.add(new Info(1, "wew"));
infoHashSet.add(new Info(2, "ete"));
infoHashSet.add(new Info(0, "qqq")); //重复元素
for (Info info : infoHashSet) {
System.out.println(info);
}
//output :
// Info [id=2, adress=ete]
// Info [id=0, adress=qqq]
// Info [id=1, adress=wew]
// Info [id=0, adress=qqq]
// 可见,重复元素被添加到了HashSet
}
/**
* 测试添加自定义对象 - 重写了equals和hashCode方法时
*/
private static void test3() {
HashSet<Info> infoHashSet=new HashSet<Info>();
infoHashSet.add(new Info(0, "qqq"));
infoHashSet.add(new Info(1, "wew"));
infoHashSet.add(new Info(2, "ete"));
infoHashSet.add(new Info(0, "qqq")); // Info重写了equals和hashCode方法,重复元素将不会再添加到set中
for (Info info : infoHashSet) {
System.out.println(info);
}
//output :
// Info [id=2, adress=ete]
// Info [id=0, adress=qqq]
// Info [id=1, adress=wew]
// 对比test2,可见HashSet不会保证元素的有序性,对于自定义对象,要保证添加到HashSet集合的元素唯一性,需重写它的equals和hashCode方法
}
}
拓展阅读: Java集合框架之Map实例解析