集合是数组的升级,数组也是一种集合,集合是一种容器,用于存放对象的容器。不同数组的是,集合可以存放不同的类型,也不限数量。如果在集合中存放基本数据会自动装箱转换成对应的引用类型。
集合存放的是对象的引用(内存地址),对象本身还是在堆中。集合也是一个引用
集合结构java.util.*
该包下存放所有的集合和集合的接口
集合又分三类,有序集合List,无序集合Set,另类是Map接口系列的集合。其中,List和Set都是Collection的子接口。而Collection又是Iterator的子接口,Iterator是迭代器,用于遍历集合中元素的接口;Map系列的集合是以键-值对存储的,Collection系列的集合是单个存储的。
不同的集合,底层对应的数据结构不同,数据结构分:数组、二叉树、链表等。数据结构不同,存储分式不同。
Collection结构图
Map结构图
以上,图中展示的是常用的类,当然还有其他的集合,其中的方法也差不多大同小异,可以自行查阅jdk文档
Q:无序不可重复,无序指的是存储和读取时的顺序,而不是排列顺序。不可重复是指存储的元素不可以重复,比如列表里已经有了1,那么再存进去1,元素个数也不会增加
Iterable接口是Collection集合的顶层接口,Iterable是迭代接口,用于获取迭代器。
主要方法:
Iterator<T> iterator()
Iterator返回一个 T 类型的迭代器
用于遍历集合中的元素。E代表泛型,用于表示未确认的数据类型。
方法
boolean hasNext()
判断容器是否还有可供访问的元素,如果具有更多元素,则返回
true
。
用于判断集合是否还有下一个元素
E next()
让迭代器向前进一位 ,并返回前进反指向的元素。
返回指向的当前元素
例:Collection集合的迭代遍历
Collection list = new ArrayList();
list.add(10.12);
list.add(true);
list.add("String");
list.add(new Object());
//获取迭代器
Iterator iterator = list.iterator();
//判断是否还有下一位元素
while(iterator.hasNext()){
//前进到下一位,并返回当前元素
Object o = iterator.next();
System.out.println(o);
}
/*
10.12
true
String
java.lang.Object@1b6d3586
Process finished with exit code 0*/
CollectionQ:迭代器最开始并不是指向集合的第一位,当第一次调用next时迭代器才会进入集合指向第一个元素
Collection下又细分Set、List两种。
List系列特点:存储的元素是有序的,先存进去的在读取时先出来,集合中的元素都有对应的下标(索引),可以存在重复的元素
Set系列特点:存储的元素无序,集合中的元素没有下标,且存储的元素不可重复
在没有使用泛型前,Collection可以存储任意的Object子类型,使用泛型后,Collection只能存储某个具体的类型。
Q:集合中不能存储基本类型,如果在添加元素时放的是基本类型,会自动装箱;集合中存储的是对象的引用,不是对象本身
常用方法boolean add(E e);
向集合中添加元素。如果添加成功,则返回true,否则反之
int size();
返回素合中的元素个数
void clear();
清空该集合的元素
boolean contains(Object o);
判断集合中是否包含指定的元素。包含返回true,否则反之
boolean remove(Object o);
删除集合中指定的元素。删除成功返回true,否则反之
boolean isEmpty();
判断集合是否为空。
Object[] toArray();
将该集合转换为一个数组,返回一个Object类型的数组
例:
Collection colls = new ArrayList();
colls.add(100);
colls.add("string");
colls.add(10.12);
colls.add(new Object());
colls.add(true);
System.out.println(colls.isEmpty());
System.out.println(colls.contains(false));
Object[] objects = colls.toArray();
for (Object o : objects) {
System.out.println(o);
}
System.out.println(colls.size());
System.out.println(colls.remove(10.12));
System.out.println(colls.size());
colls.clear();
System.out.println(colls.size());
/*
false
false
100
string
10.12
java.lang.Object@1b6d3586
true
5
true
4
0
Process finished with exit code 0*/
contains、remove方法
以ArrayList源码为例。
containspublic boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
由源码可知,contains方法底层是通过被参数去调用equals方法,从而去比较集合中的各个元素
存储到集合的元素强烈建议重写equals方法,不然在判断集合中是否包含某个对象时比较的是地址值
例:contains比较两个String元素
Collection c = new ArrayList();
String s1 = new String("123");
c.add(s1);
String x = new String("123");
System.out.println(c.contains(x));
/*
true
Process finished with exit code 0*/
c.contains(x) == x.equals(s1),而String的equals是比较内容的,也就是 “123” =?= “123” => true,所以上面返回的是true
如果比较的参数类型继承Object没有重写equals,那么其比较的必须是地址值,两个不同的对象去比较equals那么就返回false。
如果比较的参数类型重写了equals,比较的是内容,就算是两个不同的对象,只要他们的内容相同,那么就返回true
removepublic boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
ArrayList在做删除元素动作时,底层也会做equals校验
如果集合中有多个重复的元素,那么会删除第一个校验成功的元素。
Q:如果存储的元素类型没有重写equals,那么可能无法删除。因为没有重写过equals的类型,其比较的是两个对象的地址值。
例:remove删除ArrayList中的元素
Collection c = new ArrayList();
String s1 = new String("123");
c.add(s1);
String s2 = new String("123");
c.add(s2);
System.out.println(c.size());// 2
Sting x = new String("123");
c.remove(x);
System.out.println(c.size());//1
/*
2
1
Process finished with exit code 0*/
迭代器的remove方法存在重复的元素只会删除第一个与之校验成功的元素。上面删除的是‘s1’
获取迭代器Iterator时,迭代器会绑定当前状态下的集合。如果集合结构发生了改变,则需要重新获取迭代器,不然会出现:java.util.CocurrentModificationExcetion
异常。
Collection list = new ArrayList();
c.add("hello");
c.add("word");
c.add(12153);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
if (o.equals(10.12))
iterator.remove(o);
}
/*
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.collection.IteratorBase.main(IteratorBase.java:7)
Process finished with exit code 1*/
在获取了迭代器后禁止再去改变集合结构(add、remove)。因为此时的迭代器无法主动同步集合的操作,java会认为这是线程不安全的
如果想在迭代时删除集合的元素可以使用迭代器的remove方法,它会去同步集合的删除操作。
Collection list = new ArrayList();
c.add("hello");
c.add("word");
c.add(12153);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
if (o.equals(10.12))
iterator.remove(o);
}
集合遍历拓展
1.foreach循环
foreach循环可以与任何实现了Iterable接口的对象一起工作。Collection接口继承了Iterable接口,因此,类库中的任何集合都可以使用foreach做元素遍历
Collection list = new ArrayList();
list.add(10.12);
list.add(true);
list.add("String");
list.add(new Object());
System.out.println("-------------");
for (Object obj:
list) {
System.out.println(obj);
}
2.forEachRemaining方法
在Java SE 8中,还可以通过调用forEachRemaining
方法并提供一个lambda表达式完成对集合遍历
Collection list = new ArrayList();
list.add(10.12);
list.add(true);
list.add("String");
list.add(new Object());
Iterator iterator = list.iterator();
iterator.forEachRemaining(element -> System.out.println(element));
forEachRemaining方法最好不要与next方法混合使用