1、集合与数组存储数据概述:
- 1.集合、数组都是对多个数据进行存储操作的结构,简称Java容器
- 说明:此时的存储主要指的是内存层面的存储,不涉及持久化的存储(.txt,.jpg,.avi,数据库中)
2、数组存储的特点:
- 一旦初始化,其长度就确定了。
- 数组一旦定义了,其元素的类型就确定了。我们也就只能操作指定类型的数据了。
- 比如:String[] arr; int[] arr1; Object[] arr3;
3、数组存储的弊端:
- 一旦初始化了,其长度就不可修改。
- 数组提供的方法有限,对于添加、删除、插入数据等操作,非常不便。
- 获取数组中实际元素个数的需求,数组没有现成的属性或方法可用。
- 数组存储数据的特点:有序、可重复。对于无序。不可重复的需求,不能满足。
4、集合存储的优点:
5、单列集合框架
Collection接口:单列集合,用来存储一个一个的对象
- List接口:存储有序的、可重复的数据。–>“动态数组”
- Arraylist、linkedList、Vector
- Set接口:存储无序的、不可重复的数据。高中讲的集合“集合”
- HashSet、linkedHashSet、TreeSet
6、Collection接口的所有抽象方法
//boolean add(E e) 将元素e添加到集合中
//int size():获取集合中元素的个数
//boolean addAll(Collection<? extends E> c) :将c集合中的元素添加到当前的集合中
//boolean isEmpty():size == 0 判断集合是否为空
//void clear():清空集合元素
//boolean contains(Object o) 判断集合中是否包含o 底层使用equals方法
//boolean containsAll(Collection<?> c) 判断集合c中的元素是否全部在当前集合中
//boolean remove(Object o):将形参o从当前集合中移除
//boolean removeAll(Collection<?> c)差集:把当前集合中与形参c集合共有的元素移除
//boolean retainAll(Collection<?> c)交集:获取当前集合与集合c的交集,并返回给当前集合
//boolean equals(Object o):判断当前集合的所有元素与形参集合所有的元素是否相同,位置对应
//int hashCode():返回当前对象的哈希值
//集合 ---> 数组:toArray()
//数组 ---> 集合:Arrays.asList()
//iterator():返回Iterator接口的实例,用于遍历集合元素。
8、Collection集合与数组间的转换
//集合 —> 数组:toArray() Object[] obj = coll.toArray(); System.out.println(Arrays.toString(obj)); //数组 —> 集合:Arrays.asList(@NotNull T… a) List strings = Arrays.asList(“123”, "213 ", “52”); System.out.println(strings);
9、使用Collection集合存储对象,要求对象所属类满足:
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类重写equals()方法
10、遍历Collection的方式:
Collection coll = new ArrayList();
coll.add("AA");
coll.add(123);
coll.add(new Date());
coll.add(new String("Tom"));
coll.add(new Person("Jerry",12));
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
for(Object o : coll){
System.out.println(o);
}
coll.forEach(System.out::println);
11、java.utils包下定义的迭代器接口:Iterator
说明:*Iterator对象成为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素 *GOF给迭代器模式的定义为:提供一种方法访问一个一个容器(container)对象中各个元素, 而又不暴露该对象的内部细节。迭代器模式,就是为容器而生。 测试:Iterator中的remove() 如果还未调用next()或在上一次调用next()方法之后已经调用了remove()方法 再调用remove()方法,都会报IllegalStateException。
12、foreach循环
说明:
- for(集合中元素的类型 局部变量:集合或数组对象 ){
循环体 } - 代码举例
for(Object o : coll){ System.out.println(o); }
13、List接口:
存储的数据的特点:单列集合,用来存储一个一个的对象 框架:
- Collection接口:单列集合,用来存储一个一个的对象
- List接口:存储有序的、可重复的数据。–>“动态数组”
- Arraylist:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
- LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储。
- Vector:作为List接口的古老实现类;线程安全的的,效率低;底层使用Object[] elementData存储存储
源码分析:
* ArrayList源码分析:
* jdk7情况下:
* *ArrayList list = new ArrayList();
* *list.add(123)
* *...
* *list.add(12)
* *默认情况下,扩容为原来的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
* *结论:开发中,建议使用带参的构造器:ArrayList list = new ArrayList(int capacity)
*
* jdk8中ArrayList的变化:
* *ArrayList list = new ArrayList();
* *list.add(123)
* *...
* *后续的添加和扩容操作同jdk7一致
* 小结:jdk7中的ArrayList的对象的创建类似于单例模式的饿汉式,而jdk8中的ArrayList的对象
* 的创建类似于单例模式的懒汉式,延迟了数组的创建,节省了内存
*
* LinkedList的源码分析:
* *LinkedList list = new LinkedList();
* *list.add(123);
* *其中Node定义为:体现了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; *
* * } *
* *} *
* *****************************************************
*
* Vector的源码分析
* *jdk7和jdk8中铜鼓Vector()构造器创建对象时,底层都创建了长度为10的数组。
* *扩容方面,默认扩容原来长度的2倍
常用方法:
void add(int index,Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加到当前集合
int indexOf(Object obj):返回obj在集合中首次出现的位置
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index,Object obj):设置指定index位置的元素为obj,并返回此元素
List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合
总结:
* *增:add()/addAll()
* *删:remove(int index)/remove(Object obj)
* *改:set(int index,Object obj)
* *查:get(int index)
* *插:add(int index,Object obj)
* *长度:size()
* *遍历:①Iterator 迭代器方式
* ②:增强for循环 foreach方式
* ③:普通的循环
遍历的三种方式:
@Test
public void test2(){
ArrayList list = new ArrayList();
list.add(123);
list.add("AA");
list.add(true);
list.add(new Person("Tom",12));
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("*********************");
for(Object l : list){
System.out.println(l);
}
System.out.println("*********************");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
存储元素的要求:
添加到对象,所在的类需要重写equals()方法
面试题:Arraylist、linkedList、Vector的异同?
- 相同:三个类都实现了List接口,存储数据的特点相同:存储有序的,可重复的数据
- 不同:
- Arraylist:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
- LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储。
- Vector:作为List接口的古老实现类;线程安全的的,效率低;底层使用Object[] elementData存储存储
[区分List当中的remove(int index)方法和remove(Object o)方法]
代码举例:
public class ListExer {
@Test
public void test1(){
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
upDateList(list);
System.out.println(list);
}
private void upDateList(List list) {
list.remove(new Integer(2));
}
}
14、Set接口
框架:
* * |----Collection接口:单列集合,用来存储一个一个的对象
* |---Set接口:存储无序的、不可重复的数据。-->高中讲的集合“集合”
* |---HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值
* |---LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
* 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
* |---TreeSet:可以按照添加对象的指定属性,进行排序。
* 说明:Set接口中,没有额外定义新的方法,使用的都是Collection接口中声明的方法
* 要求:向Set中添加的数据,其类一定要重写hashCode()和equals()方法,以实现对象相等规则
* 要求:重写的hashCode()和equals()尽可能保持一致(相等的对象必须具有相等的散列码)。
* 重写方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值。
常用实现类: 一、Set:存储无序的、不可重复的数据
以HashSet为例说明:
- 1、无序性
无序不等于随机性 存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定 - 2、不可重复性
保证添加的元素按照equals()判断是,不能反悔true。即:相同的元素只能添加一个。
二:添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),
判断数组此位置上是否已经有元素:
*如果此位置上没有其他元素,则元素a添加成功。---情况1
*如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不同,则元素a添加成功 ---情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法,
equals()返回true,则元素添加失败
equals()返回false,则元素a添加成功。---情况3
对于添加成功的情况2和情况2而言:元素a 与已经存在指定索引位置上数据已链表的方式存储。
jdk7:元素a放到数组中,指向原来的元素
jdk8:原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。
三、LinkedHashSet的使用:
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,
记录此数据前一个数据和后一个数据
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
四、TreeSet的使用:
1.向TreeSet中添加数据,要求是相同类的对象。
2.两种排序方式:自然排序 和 定制排序
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()方法。
4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()方法。
存储对象所在类的要求:要求对象所在类重写equals()和hashCode()方法
END 视频课程笔记 课程:尚硅谷JavaSE
|