集合
1.集合框架概述
-
集合 Collection The Collection in Java is a framework that provides an architecture to store and manipulate the group of objects.
数组同样也是对多个数据进行存储操作的结构(此时的存储指内存层面的此时,不涉及持久化的存储)
-
数组存储的特点
- 长度固定:一旦初始化完毕,其长度就不可修改
- 数组类型固定:数组定义好后,只能操作指定类型的数据
-
数组存储的弊端
- 长度不可修改:初始化后,同一个 数组对象长度不可再修改
- 增删改效率低
- 缺乏属性或方法
- 无法满足无序、不可重复的需求:数组的存储特点是有序、可重复
-
集合的优点
- 动态长度
- 增删改效率高
- 相应属性或方法
- 有无序、不可重复的Set接口,也有有序、可重复的List接口
-
接口继承树
2. Collection接口
2.1 概述
Collection是单列数据,定义了存取一组对象的方法的集合。
Collection接口是List、Set和Queue接口的父接口,在JDK1.5之前,集合会丢掉容器中所有对象的数据类型,在JDK1.5加了泛型之后,集合可以记住容器中所有对象的数据类型。
2.2 方法
-
添加
add(Object obj)
addAll(Collection coll)
-
获取有效元素的个数
int size()
-
清空集合
void clear()
-
是否是空集合
boolean isEmpty()
-
是否包含某个元素
boolean contains(Object obj)
:是通过元素的equals方法来判断是否是同一个对象boolean containsAll(Collection c)
:也是调用元素的equals方法来比较的。比较集合中是否包含c集合中的所有元素 -
删除
boolean remove(Object obj)
:通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素boolean removeAll(Collection coll)
:取当前集合的差集 -
取两个集合的交集
boolean retainAll(Collection c)
:把交集的结果存在当前集合中,不影响c -
集合是否相等(注意顺序)
boolean equals(Object obj)
-
转成对象数组
Object[] toArray()
-
获取集合对象的哈希值
hashCode()
-
遍历
iterator()
:返回迭代器对象,用于集合遍历
2.3 集合和数组的转换
-
集合到数组:Collection的
toArray()
方法Collection coll1 = new ArrayList();
coll1.add(123);
coll1.add("abc");
coll1.add(new Person("Tom", 18));
Object[] os = coll1.toArray();
for (Object o : os) {
System.out.println(o);
}
/*Output
123
abc
Person{name='Tom', age=18}
*/ -
数组到集合:Arrays的
asList()
方法Collection coll2 = new ArrayList();
String[] str = new String[]{"AA", "BB", "CC"};
coll2 = Arrays.asList(str);
coll2.forEach(String -> System.out.println(String));
/*Output
AA
BB
CC
*/-
深入Arrays的asList()方法
首先查看asList()方法的源码
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}返回的ArrayList类是Arrays的私有类ArrayList,它同样实现了AbstractList接口
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
...
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
....
}可以看到,Arrays中的ArrayList和List接口的ArrayList类是不同的
String[] str = new String[]{"AA", "BB", "CC"};
//此时不再向上转型
ArrayList coll3 = new ArrayList();
coll3 = Arrays.asList(str);//报错
-
3.Iterator迭代器接口
3.1 概述
-
Iterator迭代器主要用于遍历Collection集合中的元素
在设计模式中,迭代器提供一种方式访问一个容器对象中各个元素,而又不暴露该对象的内部细节
-
Collection接口继承了java.lang.Iterable接口,该接口有一个Iterator()方法,每次调用Iterator()方法都会返回一个全新的Iterator对象
3.2 方法
Iterator方法
Modifier and Type | Method and Description |
---|---|
default void | forEachRemaining(Consumer action) Performs the given action for each remaining element until all elements have been processed or the action throws an exception. |
boolean | hasNext() Returns true if the iteration has more elements. |
E | next() Returns the next element in the iteration. |
default void | remove() Removes from the underlying collection the last element returned by this iterator (optional operation). |
Code
使用hasNext()、next()、remove()方法
public class IteratorTest {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Hello"));
coll.add(false);
Iterator iterator = coll.iterator();
//remove方法
while (iterator.hasNext()){
Object o = iterator.next();
if(o.equals(123)){
iterator.remove();
}
}
iterator = coll.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
/*Output:
456
Person{name='Jerry', age=20}
Hello
false
*/
}
}
3.3 遍历集合的四种方式
先构建一个集合,并添加测试数据
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Hello"));
coll.add(false);
方式一:使用Iterator结合while循环
Iterator iterator = coll.iterator();
//方式一:使用while循环
while(iterator.hasNext()){
System.out.println(iterator.next());
}
方式二:使用foreach语句,本质还是调用Iterator迭代器
for (Object o : coll){
System.out.println(o);
}
方式三:使用Iterator中的forEachRemaining()方法结合Lambda表达式
Iterator iterator = coll.iterator();
//方式三:使用forEachRemaining()方法
iterator.forEachRemaining((Object o)-> System.out.println(o));
//再次遍历输出为空
iterator.forEachRemaining((Object o)-> System.out.println(o));
方式四:使用集合中的forEach()方法结合Lambda表达式
//方式四:使用forEach()方法
coll.forEach((Object o)-> System.out.println(o));
//forEach()方法可多次遍历
coll.forEach((Object o)-> System.out.println(o));
方法引用:
coll.forEach(System.out::println);
参考博客:方法引用
4.Collection子接口之一:List接口
4.1 List接口概述
- List中元素可有序、可重复
4.2 List接口方法
List除了Collection接口外,还增加了一些根据索引来操作集合元素的
void add(int index, Object ele)
:在index位置插入ele元素boolean addAll(int index, Collection eles)
:从index位置开始将eles中 的所有元素添加进来Object get(int index)
:获取指定index位置的元素int indexOf(Object obj)
:返回obj在集合中首次出现的位置int lastIndexOf(Object obj)
:返回obj在当前集合中末次出现的位置Object remove(int index)
:移除指定index位置的元素,并返回此元素Object set(int index, Object ele)
:设置指定index位置的元素为eleList subList(int fromIndex, int toIndex)
:返回从fromIndex到toIndex位置的子集合
4.3 List三个实现类
- java.util.Arraylist:JDK1.2开始,最主要、最常用的实现类,底层使用Object[] elementData存储,线程不安全但效率高
- java.util.LinkedList:JDK1.2开始,一般使用在频繁增删的场景,底层使用双向链表存储
- java.util.Vector:JDK1.1开始,线程安全但效率低,底层也是使用Object[] elementData存储
4.3.1 List实现类之一:ArrayList
- JDK1.8之前:类似饿汉式,直接创建容量为10的Object数组,扩容时以原先数组的1.5倍起
- JDK1.8:类似懒汉式,第一次调用时才创建容量为10的Object数组,扩容时同JDK1.8之前
4.3.2 List实现类之二:LinkedList
底层是双向链表
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
4.3.3 List实现类之三:Vector
不常用
5. Collection子接口之二:Set接口
5.1 Set接口概述
5.1.1 Set特性
-
Set中元素无序、不可重复
- 无序性:Set的底层也是数组,它存储数据的规则是按照数据的哈希值来决定的,所以这也决定了元素存放位置的数组索引不等于添加顺序。
- 不可重复:保证添加的元素按照
equals()
方法判断时,不能返回true
-
Set判断两个对象相关不是用
==
,而是用equals()
方法Set判断两个对象相关是用
equals()
方法,如果类没有重写equals()
方法,那么它会调用Object类中的equals()
方法,Object的equals()
方法底层仍是使用==
-
Set中没有额外定义新的方法,使用的都是Collection声明的方法
5.1.2 Set添加元素的过程
以HashSet为例:
向HashSet添加元素a时,首先调用元素a所在类的hashCode()方法,得到a的hash值,接着使用该hash值经过某种算法计算出a应该在HashSet底层数组的存放位置
如果该存放位置为空,则直接添加元素a
如果存放位置有b,则调用a所在类的equals方法进行比较
如果相等,则添加失败
如果不等,则添加a(以链表的方式添加,JDK8中会让b仍存在原来的数组中,然后指向a)
5.2 Set三个实现类
- java.util.HashSet
- 作为Set的主要实现类
- 线程不安全
- 可以存储一个
null
值
- java.util.LinkedHashSet
- HashSet的子类
- 遍历内部数据时,可以按照添加时的顺序遍历
- java.util.TreeSet
- 可以按照添加对象的指定属性进行排序
5.2.1 Set实现类之一:HashSet
5.2.2 Set实现类之二:LinkedHashSet
- HashSet的子类
- LinkedHashSet使用双向链表维护元素的次序
- 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
5.2.3 Set实现类之三:TreeSet
- 只能添加相同类的对象
- 会按照添加对象的属性去排序
CODE
代码一
public class TreeSetTest {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(129);
set.add(-1);
set.add(30);
set.add(4);
set.forEach((o)-> System.out.println(o));
/*Output:
-1
4
30
129
*/
}
}
代码二
让Person类实现Comparable接口,实现接口的compareTo()
方法,比较name
public class Person implements Comparable{
private String name;
private int age;
//省略
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person person = (Person)o;
return this.name.compareTo(person.name);
}else{
throw new RuntimeException("The type of input is mismatch################");
}
}
}
在TreeSet中添加Person类
public class TreeSetTest {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(new Person("Tom",13));
set.add(new Person("Jerry",2));
set.add(new Person("Mike",31));
set.add(new Person("Jack",80));
set.forEach((o)-> System.out.println(o));
/*Output
Person{name='Jack', age=80}
Person{name='Jerry', age=2}
Person{name='Mike', age=31}
Person{name='Tom', age=13}
*/
}
}
6.Map接口
6.1 概述
Map和Collection并列存在,所以称呼集合并不准确,更好的应该是称呼为容器,集合是容器的一个子集。
Map数列数据,用于保存具有映射关系的键值对:key-value
Map中的key用set进行存放,不允许重复,
key所在的类要重写
equals()
方法和hashCode()
方法,而value所在的类要重写equals()
,hashCode()方法在储存时被调用
常用的实现类是HashMap
主要实现类:
-
HashMap:线程不安全,效率高;可以存储
null
的key和valueJDK1.8之前,HashMap底层是数组+链表,JDK1.8之后,底层是数组+链表+红黑树
-
子类LinkedHashMap:遍历元素时,可以按照添加的顺序实现遍历,其原理同LinkedHashSet一样,每个元素添加一对指针来维护其顺序
当频繁增删数据时,LinkedHashMap效率高于HashMap
-
-
TreeMap:保证按照添加的key-value进行排序,实现排序遍历,此时考虑key的自然排序或者定制排序
-
HashMap:线程安全,效率低;可以存储null的key和value
- 子类Properties:常用来处理配置文件,key和value都是String类型
public class TestMap {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
Map<String,Integer> map2 = new Hashtable<>();
map.put(null, null);
map.put("1",null);
// map2.put(null, null); //编译通过,运行时报空指针异常
// map2.put("1",null); //同样报空指针异常
}
}