集合Collection、List、Set详解
java高级之集合
概述
- 万事万物皆对象,我们可以使用数组来存储对象但存在一些弊端。
- 集合就像是一种java容器,可以动态的存储多个对象,可以把它理解为是动态的数组。
- 集合框架包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
集合 VS 数组
数组
- 数组存储的数据有序、可重复;
- 数组初始化以后,长度确定,不可变,不易扩展;
- 数组声明的类型,元素初始化时的类型便确定;
- 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,效率不高;无法直接获取存储元素的个数。
集合
- 长度可变;
- 可以存储不同的数据类型。
整体概述
-
Collection
接口,理解为单列集合,存储对象时以单个来存储。 -
Map
接口:理解为双列集合,用来存储一对(key - value)一对的数据。
Collection接口
常用方法
Collection
接口常用方法总结。
方法返回值 | 方法名 | 方法的描述 |
---|---|---|
boolean |
add(Object e) |
添加单个元素 |
boolean |
addAll(Collection c) |
指定集合的元素添加到此集合 |
int |
size() |
获取集合中的元素个数。 |
void |
clear() |
清空集合 |
boolean |
isEmpty() |
判断该集合是否为空 |
boolean |
contains(Object o) |
判断是否包含该元素 |
boolean |
containsAll(Collection c) |
判断是否包含指定集合 |
boolean |
remove(Object o) |
通过equals()方法找到指定的第一个元素并删除 |
boolean |
removeAll(Collection<?> c) |
删除指定集合在本集合中的元素 |
boolean |
retainAll(Collection<?> c) |
取交集, c中存在的元素 |
boolean |
equals(Object o) |
判断集合是否相等,元素都相同时才相等 |
int |
hashCode() |
返回此集合的哈希码值。 |
Object[] |
toArray() |
返回一个包含此集合中所有元素的数组。 |
Iterator<E> |
iterator() |
返回迭代器对象 |
-
代码试验如下:
@Test public void test1(){ Collection collection = new ArrayList(); //1、add(Object e) 添加单个元素 collection.add("AA"); collection.add(new Student("张三",24)); Collection collection1 = new ArrayList(); collection1.add("BB"); collection1.add("CC"); //2、addAll(Collection c) 指定集合的元素添加到此集合 collection.addAll(collection1); //3、size() 获取集合中的元素个数 collection.size(); //打印一下该集合 System.out.println(collection); //4、clear() 清空集合 //collection.clear(); //5、isEmpty() 判断集合是否为空 System.out.println("集合是否为空:"+collection.isEmpty()); //6、boolean contains(Object o); 判断是否包含该元素 System.out.println(collection.contains("AA")); //如果不重写equals()和hashcode()方法,会是false System.out.println(collection.contains(new Student("张三",24))); //7、boolean containsAll(Collection c) 判断是否包含指定集合 System.out.println(collection.containsAll(collection1)); //8、boolean remove(Object o),通过equals()方法找到指定的第一个元素并删除 collection.remove("AA"); //9、boolean removeAll(Collection coll) 删除指定集合在本集合中的元素 collection.removeAll(Arrays.asList("BB","CC")); //打印一下该集合 System.out.println(collection); collection.add("AA"); collection.add("BB"); collection.add("CC"); //10、boolean retainAll(Collection c) 取交集 collection.retainAll(Arrays.asList("BB","CC")); //打印一下该集合 System.out.println(collection); //11、boolean equals(Object obj) 判断集合是否相等,元素都相同时才相等 System.out.println(collection.equals(Arrays.asList("BB","CC"))); //12、返回该集合的hashCode() System.out.println(collection.hashCode()); //13、Object[] toArray(), 返回一个包含此集合中所有元素的数组。 Object[] arr = collection.toArray(); for (Object o : arr) { System.out.println(o); } }
迭代器
-
设计模式中有一种模式叫做迭代器模式,它的定义为:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
-
迭代器即
Iterator
对象,Collection
接口继承了java.lang.Iterable
接口,该接口有一个iterator()
方法,那么所有实现了Collection
接口的集合类都有一个iterator()
方法,用以返回一个实现了Iterator
接口的对象。 -
集合调用
iterator()
方法,返回Iterator
对象,该对象用于遍历集合,而集合对象每次调用该方法时,返回的是一个全新的迭代器。
迭代器中的方法
-
迭代器中的方法
方法的返回值 方法 方法的描述 boolean
hasNext()
判断迭代器是否有下一个元素 E
next()
返回迭代中的下一个元素。 default void
remove()
在底层集合中删除元素 -
测试如下
@Test public void testIterator(){ Collection collection = new ArrayList(); collection.add("AA"); collection.add("BB"); collection.add(123); collection.add(456); collection.add(new Student("Jerry",20)); collection.add(false); //14、iterator() 返回此集合中的元素的迭代器 //每次调用时都得到一个全新的迭代器对象,默认游标在集合的第一个元素之前。 Iterator iterator = collection.iterator(); // 14.1 hasNext():判断该集合是否有下一个元素 while(iterator.hasNext()){ // 14.2 next(): // 1、指针(游标)下移 // 2、将游标下移以后集合位置上的元素返回 System.out.println(iterator.next()); } System.out.println("============================================"); //得到该集合的迭代器对象 Iterator iterator1 = collection.iterator(); while(iterator1.hasNext()){ String b = (String)iterator1.next(); if (b.equals("BB")) { //14.3 remove() 删除底层集合中的该元素 //详情见下面的Arraylist 中iterator() 方法 iterator1.remove(); } System.out.println("当前元素为:" + b); } }
源码简单分析
-
以
Arraylist
中iterator()
方法为例,简单分析如下://Arraylist中的iterator()方法 public Iterator<E> iterator() { return new Itr(); } //私有内部类 private class Itr implements Iterator<E> { //游标,用于记录要返回的元素的索引 int cursor; //返回的最后一个元素的索引;如果没有这个值就是-1 当执行remove()方法时,置为-1 int lastRet = -1; //modCount 是集合的修改次数,当进行add或者remove时,modCount会+1; //expectedModCount是指集合的迭代器的版本号,初始值是modCount; //使用集合中的add或者remove方法时,expectedModCount不会变; //使用迭代器的add或者remove方法时,会同步expectedModCount。 int expectedModCount = modCount; Itr() {} public boolean hasNext() { return cursor != size; } //每次执行一次 cursor+1 返回元素 @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } //迭代器中的remove(), 使用ArrayList的remove()方法, //但是会修正次数,expectedModCount = modCount; //并且将 lastRet = -1,所以不能连续使用remove()方法。 public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } ..... //校验 错误的检测,通过比较:modCount != expectedModCount //在使用集合的add()、remove()、clear()方法时,modCount++ final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
循环
-
数组的增强for循环使用的是普通的for 循环,在循环体中给数组赋值时数组会改变。
-
集合使用的是迭代器的for循环。
//for(集合元素的类型 局部变量 : 集合对象) //内部仍然调用了迭代器。 for (Object o : collection) { System.out.println(o); } //编译器在看到一个实现了Interator接口的对象的增强for循环时,会自动地重写,变成使用迭代器来遍历集合。 Iterator<Integer> iterator=collection.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
List接口
概述
-
List
接口是Collection
接口的子接口; -
List
集合类中元素有序、可重复,集合中的每个元素都有其对应的顺序索引用以记载其在容器中的位置,可以根据序号存取容器中的元素; -
List
接口的常用实现类:ArrayList
、LinkedList
和Vector
。
常用方法
List
接口存储的元素有序、可重复,故拥有除下Collection
接口方法之外的其他方法,还有如下:
方法返回值 | 方法名 | 方法的描述 |
---|---|---|
void |
add(int index, E element) |
将指定的元素插入该集合的指定位置 |
boolean |
addAll(int index, Collection c) |
指定集合中的所有元素插入到该集合的指定位置 |
E |
get(int index) |
返回此列表中指定index位置的元素。 |
int |
indexOf(Object o) |
指定元素首次出现的索引,否则返回-1 |
int |
lastIndexOf(Object o) |
指定元素的最后一次出现的索引,否则返回-1 |
E |
remove(int index) |
删除该列表中指定索引的元素。 |
E |
set(int index, E element) |
用指定的元素,替换指定索引的元素。 |
List<E> |
subList(int fromIndex, int toIndex) |
返回此列表中左闭右开的子集合 |
-
代码试验如下:
@Test public void testList(){ ArrayList list = new ArrayList(); list.add("AA"); list.add("BB"); list.add(123); list.add(456); list.add(new Student("tom",18)); //1、void add(int index, E element) 将指定的元素插入该集合的指定位置 list.add(2,"CC"); //2、addAll(int index, Collection c) 指定集合中的所有元素插入到该集合的指定位置 list.addAll(5,Arrays.asList(78,12,456)); System.out.println(list); //3、E get(int index) 返回此列表中指定index位置的元素。 System.out.println(list.get(1)); //4、int indexOf(Object o) 指定元素首次出现的索引,否则返回-1 System.out.println(list.indexOf("CC")); //5、int lastIndexOf(Object o) 指定元素的最后一次出现的索引,否则返回-1 System.out.println(list.lastIndexOf(456)); //6、E remove(int index) 删除该列表中指定索引的元素。 list.remove(6); //7、E set(int index, E element) 用指定的元素,替换指定索引的元素。 list.set(0,"AAA"); System.out.println(list); //8、List<E> subList(int fromIndex, int toIndex) 左闭右开的子集合 List sublist = list.subList(3,7); System.out.println(sublist); }
实现类:ArrayList
-
ArrayList
是List
接口的典型实现类、主要实现类,它本质上是对象引用的一个可变的数组。 -
它的底层实现是数组,不同步,非线程安全,效率高,支持随机访问,查询快,增删慢。
-
源码简单分析
//底层是数组 底层Object[] elementData初始化为{} public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //首次添加add()时,数组长度创建为10,并将索引为0的位置添加上元素 //多次添加后,(到11),扩容,为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。 public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; }
实现类:LinkedList
-
LinkedList
底层实现是双向链表,不同步,非线程安全,效率高,查询慢,增删快。 -
它内部定义了
Node
类型的first
和last
,用于记录首末元素。同时,定义内部类Node
,作为LinkedList
中保存数据的基本结构。
实现类:Vector
Vector
底层实现是数组,同步,线程安全,效率低,查询快,增删慢。扩容机制是扩容为原来的两倍。
List接口总结
ArrayList
和Vector
底层是基于数组实现,查询快;LinkedList
底层基于双向链表,增删快。
Set接口
概述
-
Set
接口是Collection
接口的子接口; -
Set
集合类中元素无序、不可重复;- 无序性:存储数据时,按照数据的哈希值来决定该数据在底层数组中的位置,无序性不代表随机性。
- 不可重复性:添加的数据不重复。详情见下面添加数据的过程。
-
Set
接口的常用实现类:HashSet
、HashSet
的子类LinkedHashSet
和TreeSet
。
实现类:HashSet
-
HashSet
是Set
接口的典型实现,使用Hash
算法来存储集合中的元素,存取、查找、删除性能高; -
底层实现是:
HashMap
,无序、不重复、不同步、线程不安全、允许null值; -
HashSet
添加数据的过程,即如何判断数据是否相同?-
在
HashSet
中添加元素A时,调用A的hashCode()
方法计算出哈希值,该哈希值使用某种算法计算出在底层数组中的存放位置,然后判断该位置是否有元素:-
情况1:没有元素、添加成功;
-
情况2:有其他元素M,比较A与M两者的
hash
值,- 情况1:不相同:添加成功,
- 情况2:
hash
值相同,调用A元素的equals()
方法,- 情况1:返回
true
,说明元素相同,添加失败, - 情况2:返回
false
,说明元素不相同,添加成功。
- 情况1:返回
-
-
实现类:LinkedHashSet
LinkedHashSet
是HashSet
的子类,它根据元素的hashCode
值来决定元素的存储位置,与此同时,使用双向链表来维护元素的次序,插入性能低于hashSet
,- 底层实现是:
LinkedHashMap
,有序,记录元素插入的顺序、不允许重复、不同步,线程不安全、允许null值。
实现类:TreeSet
-
TreeSet
确保集合元素处于排序状态,底层实现是:红黑树,不允许重复。 -
有序
-
自然排序(默认排序):对象相等标志:
compareTo()
返回0。//学生类实现Comparable 重写compareTo方法。 public class Student implements Comparable{ @Override public int compareTo(Object o) { if(o instanceof Student){ Student student = (Student)o; int compare = -this.name.compareTo(student.name); if(compare != 0){ return compare; }else{ return Integer.compare(this.age,student.age); } }else{ throw new RuntimeException("输入的类型不匹配"); } } }
-
定制排序:对象相等标志,
compare()
返回0。Comparator comparator = new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof Student && o2 instanceof Student){ Student s1 = (Student)o1; Student s2 = (Student)o2; return Integer.compare(s1.getAge(),s2.getAge()); }else{ throw new RuntimeException("输入的数据类型不匹配"); } } };
-
Set接口总结
HashSet
是最常用的Set,存储无序不可重复的数据;LinkedHashSet
使用双向链表维护元素的次序;TreeSet
有两种排序方式:自然排序和定制排序来保证元素有序。
原创不易,欢迎转载,转载时请注明出处,谢谢!
作者:潇~萧下
原文链接:https://www.cnblogs.com/manongxiao/p/13547776.html