a.
对象数组 --自定义对象类
概述
* 创建一个自定义类用于存储学生信息,并且获取到每个学生信息 。
使用
Student[] s = new Student[5]; //创建student类,用于存储数组对象。
s[0] = new Student("hah", 2); //第一个Student数组的元素,且每个地址值有所不同
s[1] = new Student("fas", 4); // 数组中存储的元素记录的都是地址值,通过每个地址值找到一个个的new的元素对象。
s[2] = new Student("fasd",34);
s[3] = new Student("we", 43);
s[4] = new Student("ad", 23);
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
注意
* 数组和集合存储 引用数据类型 的时候,存储都是地址值,且每个地址值都有所不同,通过地址值来查找每个元素对象。
* 自定义对象数组中 存储的元素记录的其实都是地址值,即每个元素对象的地址值。 通过每个索引地址值找到每个 new的元素对象。
b.
集合
--由来
* 前提:数组和集合都是容器。
* 因为数组的长度是固定的,当添加的元素超过数组的长度需要对数组进行重新定义,这样太麻烦。 所以Java在jdk1.2的时候为我们提供了集合类。集合:用于存储任意对象,而且长度可以改变,是随着元素的增加而增加,随着元素的减少而减少的,即随着元素个数的变化而变化。
--概述
* 数组和集合都是容器。
* 集合于jdk1.2时被提出,长度可改变,是随着元素个数的变化而变化。
数组和集合区别
* 可以从两方面来说:A存储的类型 和 B存储的长度:
* 区别:
区别1:
* 数组既可以存储基本数据类型,也可存储引用数据类型。基本数据类型存储的是值,而引用数据类型存储的是地址值。
* 集合只能存储引用数据类型(对象)集合也可以存储基本数据类型,但存储的时候会自动装箱变成对象,即int类型的100,存储到集合中就变成了new Integer(100)。
区别2:
* 数组的长度是固定的,不可自动增加。
* 集合的长度是可变的,是根据元素个数的变化而变化。
--数组和集合什么时候使用
* 如果元素个数固定,就用数组。
* 如果元素个数未知,不是固定,就用集合。 --部分集合底层代码还是用数组来实现的,如果超出了数组的范围,就创建一个 大于前者的1.5倍长度的数组。增长后,将原数组变成垃圾,以此类推。
--集合继承体系关系图
-Collection --单列集合根接口
* List -存取有序,和数组相同。有索引,可存储重复元素。
* ArrayList 底层 数组实现。
* LinkedList 底层 链表实现。
* Vector(jdk1.0) 底层 数组实现。 --已被ArrayList替代。 之所以和集合命名规则不同,是集合体系是jdk1.2时候被提出,而jdk1.0的时候已经有了Vector,因为Vector中的功能和集合一样,所以让Vector也实现了List接口并且并入到了集合体系中。
* Set -存取无序,无索引,不可存储重复元素。
* HashSet 底层 Hash算法。 元素无序。
* LinkedHashSet 底层 链表结构,是set集合中唯一一个怎么存怎么取的集合,又因为是HashSet的子类,所以同样保证了元素的唯一性。
* TreeSet 底层 二叉树算法。 元素可以进行排序。
-Map --双列集合根接口
* HashMap 底层 哈希算法。 保证键的唯一性。
* TreeMap 底层 二叉树。 键可以排序。
c.
Collection
--概述
* Collection是所有 单列集合 的根类接口。
* Map是所有 双列集合 的根接口。
* 集合是jdk1.2的时候提出。
* 集合的命名都有一定的规范:底层是怎么实现的,前者单词就是什么,后者是哪个继承体系下的。后者单词就是什么。
如:
ArrayList 底层数组实现,属于List集合下。
LinkedList 底层链表实现,所以List集合下。
--体系结构图:
Collection(单列集合根接口)
* List(有序,有索引,可重复元素)
* ArrayList 数组实现。存取有序,和数组一样。有索引,可存储重复元素。
* LinkedList 链表实现。
* Vector(jdk1.0) 数组实现。之所以和集合命名规则不同,是因为在jdk1.0时候出现,而集合体系是jdk1.2时候出现,已被ArrayList替代。
* Set (无序,无索引,不可重复元素)
* HastSet 哈希算法。
* TreeSet 二叉树算法。
注意:List是一个不可插队的插入对象。
d.
Collection集合的基本功能
概述
* util包下,用时需导包。
* Collection 是接口,用时只能:父类引用指向子类对象,即: Collection c = new ArrayList();
* Collection 单列集合 中的根接口。
* Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素(list),而另一些则不允许(set)。一些collection 是有序的(list),而另一些则是无序的(set)。
* JDK不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。
常用方法
* boolean add(Object obj); 添加对象 可添加任意对象。
* 结果Boolean永远都是true。那什么时候是false? 只有当set集合存储重复元素的时候,返回false。
* 所以一般我们不用返回值类型,自己将对象添加到集合中即可。即:c.add("sfa");
*
* boolean remove(Object obj); 删除对象 可删除任意对象。可以是基本数据类型,到时候会自动装箱成对象。
* void clear(); 清空集合元素
* boolean contains(Object o); 判断是否包含指定元素
* boolean isEmpty(); 判断集合是否为空,为空就返回true,否则为false。
* int size(); 获取集合中的元素个数
注意:1)add如果是List集合,就一直返回true,因为list集合中是可以存储重复元素的。
如果是set集合用add存储重复元素,就会返回false。
2)ArrayList的父类的父类重写了toString方法,所以在打印对象的引用时输出的结果不是Object的 toString 的结果,而是集合特有的:[元素]的形式。
collectionXxx.java使用了未经检查或不安全的操作.
注意:要了解详细信息,请使用 -Xlint:unchecked重新编译.
java编译器认为该程序存在安全隐患
温馨提示:这不是编译失败,所以先不用理会,等 学了泛型你就知道了
toArray() 集合的遍历之集合转数组遍历的功能
集合遍历
* 就是依次获取集合中的每个元素。
* 集合转数组,就可以实现对集合的遍历。
* Object[] toArray(); 集合转数组的方式就可以遍历集合中的每个元素。
代码
//自定义 Bean对象 的遍历方式。
Collection c2 = new ArrayList(); //创建单列集合对象。
c2.add(new Student("哈哈",23)); //等同于 Object o = new Student("哈哈",23);
c2.add(new Student("嘻嘻",25));
c2.add(new Student("么么",332));
c2.add(new Student("啦啦",645));
Object[] array2 = c2.toArray();
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]); //获取集合中所有的元素对象。
// 如果想获取每个元素对象中的Name 或 age,就要向下转型,即Object转成Student对象。现在的array2[i] 就是Ojbect对象。
// (看左边的类型,如果左边是Object对象 就是向上转型,如果左边是Student对象,就是向下转型)
Student st =(Student)array2[i]; //向下转型。将Object的array2转成Student。
System.out.println("姓名:"+st.getName()+" 年龄:"+st.getAge());
}
Collection集合中方法带有All后缀的功能
* boolean addAll(Collection c); 添加集合。
* 如:
* c1集合有对象:abcd c2集合有对象:ef
* c1.addAll(c2); 将c2集合中的每个对象添加c1中,结果为:[a,b,c,d,e,f]
* c1.add(c2); 将c2看做是一个整个对象添加到c1中,结果为:[a,b,c,d,[e,f]]
* boolean removeAll(Collection c); 删除集合或删除集合的元素,删除的是两个集合的交集为true,如果删除的是两个集合没有的交集,就是false。
* boolean containsAll(Collection c); 是否一个集合包含另一个集合的所有元素。如果全包含就为true,否之为false。
* boolean retainAll(Collection c); 取出两个集合之间的交集。
* 注意:如果调用的集合改变就返回true, 如果调用的集合没有改变就返回false。
如:
1) c1集合有:abcd c2集合有 ab c1.retainAll(c2); 取交集c1结果为:ab,c1改变了,所以返回true。
2) c1集合有:abcd c2集合有 z c1.retainAll(c2); 取交集c1结果为:[], 但c1还是改变,所以也返回true。
3) c1集合有:abcd c2集合有 abcdefg c1.retainAll(c2); 取交集c1结果为:abcd,c1没有改变,所以为false。
这就是retainAll的特殊之处,主要是 前者跟后者比较,前者的结果是否改变,如果改变就是true,如果没变就是false。
Collection集合的Iterator迭代器的遍历方式
概述
* 集合用于存储元素,存储的元素如果需要查看,就要使用迭代器的方式。
使用
* 使用 Iterator 的方式来迭代。
* 关键方法:
* hasNext(); 判断是否还有下一个元素,如果有就返回true。
* next(); 返回迭代的下一个元素,即将集合中的元素取出。
* next具有将指针向下移一位的功能。
Collection存储自定义对象并遍历
Collection c = new ArrayList(); // 创建集合对象。父类引用指向子类对象
c.add(new Student("赵白石",43)); //等等于: Object obj = new Student("赵白石",43); 即父类引用指向子类对象,obj不能使用子类特有的属性和行为,即使用只能靠向下转型的方式才能够子类特有的属性和行为。
c.add(new Student("沈星移",33));
c.add(new Student("周莹",40));
c.add(new Student("吴聘",42));
// 方式一:while来做
/* Iterator it = c.iterator();
while(it.hasNext()){
Student st = (Student)it.next(); //向下强转。 将it.next的Iterator的对象向下强转成Student对象。只有向下转型,才能使用子类特有的属性和行为。
String name = st.getName();
int age = st.getAge();
System.out.println("姓名:"+name+".....年龄:"+age);
}*/
//【注意:向下转型,才能使用子类特有的属性和行为】
// 方式二:for循环来做
for (Iterator it = c.iterator(); it.hasNext();) {
Student s = (Student)it.next(); //向下强转。将it.next的Iterator的对象向下强转成Student对象。这样就能获取子类特有的属性和行为。
System.out.println(s.getName()+"..."+s.getAge());
}
}
--迭代器的原理及源码解析
原理
迭代器是对集合进行遍历的,而每个集合内部的存储结构都是不同,所以每个集合的存和取的方式也都不同的。那么就需要在每一个类中都去定义hasNext()方法 和 next()方法,这样做可以方便的判断是否有下一个元素和返回集合中的当前元素。但是这样整个集合体系看起来就会过于臃肿。所以就将迭代器向上抽取出一个接口,然后在每个类的内部去定义自己的迭代方式。 这样做的好处有二:
第一:规定了整个集合体系的遍历方式通过通过hasNext()和next()方法来实现的。第二:代码由底层内部实现,使用者无需关心怎么实现的,会用即可。
迭代器的原理:
迭代器是用来遍历集合。因为集合的底层数据结构不同,就导致集合存取元素的方式不同,如果每个集合类都定义自己的存取方式,这样做是可以的,但是会让整个继承体系很冗余,臃肿。所以我们经过不断的向上抽取,最终定义成了接口。
这样做的好处有两个:
A:更规范。
B:我们不用管底层是怎么实现,只要会用就可以了。
迭代器源码解析
* 1,在eclipse中ctrl + shift + t找到ArrayList类
* 2,ctrl+o查找iterator()方法
* 3,查看返回值类型是new Itr(),说明Itr这个类实现Iterator接口
* 4,查找Itr这个内部类,发现重写了Iterator中的所有抽象方法
Iterator iterator(); 该对象必须依赖具体的集合容器,因为每个集合容器的内部数据结构不同,所以该迭代器对象是在集合容器中进行内部实现的,也就是说iterator()方法在每个集合容器中的实现方式时不同的。
对于调用者来说,具体的实现过程无需知道,只要通过该集合容器获取到该实现的迭代器对象即可,也就是iterator方法。
Iterator接口就是对所有单列集合根接口Collection容器进行元素取出的公共接口。
e.
List集合
概述
* List是一个接口,在util包下,使用的话,只能父类引用指向子类对象,如下:
* List i = new ArrayList(); 但这样做存在弊端,不能使用子类特有的属性和方法。
* 建议:ArrayList al = new ArrayList(); // 这样写。直接new对象即可直接使用子类属性和方法。
* 因为多态当 [参数] 用是比较合适。因为可以更好的扩展代码功能。
* 有序集合,有索引,可存储重复元素。
* ArrayList底层 数组实现。
方法
* void add(int index, E element); 在集合的指定索引位置上添加E对象。E表示泛型表示任意对象。
* 注意:
* 当添加时使用不存在的索引,就会出现:IndexOutOfBoundsException 角标越界异常。
* 添加的索引是: index<=size 并且index>=0 在这个范围内 就都不会出错报异常。
* E remove(int index); 通过集合的索引删除元素,将被删除的元素返回。
* * remove的过程不会将索引自动装箱成对象。
* E get(int index); 获取集合指定索引上的元素对象。
* get(int index)方法可作为for循环变量集合的其中一个条件,配合size()方法便可遍历出具体集合元素对象。
* E set(int index, E element); 设置集合索引上的元素。
* int indexOf(Object o); 返回此列表中第一次出现指定元素的索引位置,如果没有就返回-1
* List subList(int fromIndex. int toIndex); 返回列表中从指定位置开始到指定位置结束之间的部分视图。含头不含尾的。
List集合的遍历
概述
* 通过get() 和 size()方法结合使用遍历。
* 代码
List list = new ArrayList(); //创建集合对象。
list.add(new Student("aa",33)); //添加Student元素对象,等于 Ojbect o = new Student("aa",33)
list.add(new Student("bb",23));
list.add(new Student("cc",13));
//遍历集合,使用size()和get()方法去遍历。
for(int i=0;i<list.size();i++){
Object obj = list.get(i); //获取每个集合元素对象。
sop(obj);
//如果想获取具体Student对象的name和age属性,就要向下强转对象,再获取。因为父类不能使用子类特有的属性和行为。
Student st = (Student)obj; //向下强转对象。
sop(st.getName()+"..."+st.getAge());
}
List集合 --并发修改异常产生的原因及解决方案
* 需求
* 我有一个集合,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。
* 问题出现
* 如果使用Iterator普通迭代器 来遍历集合且同时添加元素对象时,就会出现 并发修改异常,即ConCurrentModificationException。
* 当使用Iterator普通迭代器 来作为遍历的方式时,一边遍历元素对象并且一边添加元素对象,就会出现这样的异常。
* 解决方案
* 使用List集合特有的一种迭代方式:ListIterator。
* ListIterator迭代器可以 一边遍历集合,一边使用ListIterator迭代器可以特有的add方法添加元素(不是ArrayList的add方法),这样才不会有 并发修改异常 的出现。
List集合中特有的迭代器 ListIterator 的了解:
* boolean hasNext(); 正向顺序判断是否还有下一个元素。
* boolean hasPrevious(); 逆向顺序判断是否还有前一个元素。
* Object next(); 正向顺序返回下一个元素。
* next()获取元素并将指针向后移动一次。
* Object previous(); 逆向顺序返回上一个元素。
* previous()获取元素并将指针向前移动一次
注意:必须先有正向遍历,才可以逆向遍历。
Vector(1.0)
概述
* 有序集合,在util包下,是一个类。
* 在jdk1.2出现后的集合体系将Vector并入里面,且用 ArrayList代替这个集合了。
* 底层 数组实现。
* 同样具有 Iterator 迭代器,但该迭代器不是自己的,而是从父类接口List继承下来的。
* 他有自己遍历集合的方式:Enumeration
特有功能
* void addElement(E obj); 在没有并入集合系统时,使用的是这个方法添加元素,当并入以后就是用 add() 去添加元素。
* E elementAt(int index); 在没有并入集合系统时,使用的是这个方法添加元素,当并入以后就是用 nextElement() 去添加元素。
* Enumeration elements(); 获取枚举 等同于 Iterator迭代器。
【遍历集合的几种方式】
list
* 使用 toArray() 集合转数组的方式 + 普通for循环。
* 使用 List中的 size() 和 get(int index) 的方式for循环获取集合元素。
* 使用 Iterator迭代器, 直接遍历集合获取元素,如果普通迭代器 一边遍历集合元素,一边添加元素,这样会一定出现:并发修改异常:ConcurrentModificationException。
* 使用 List集合中特有的:ListIterator迭代器,一边遍历集合元素,一边使用ListIterato特有的add()方法添加(修改)元素的方式,这样就不会出现 并发修改异常。
vector
* Iterator迭代器
* Vector vec = new Vector(); Enumeration ele = vec.elements(); 获取枚举的方式。
f.
数组结构之数组和链表 2017/10/5 0:24:53
概述
* 底层:数组 --ArrayList,Vector
* 查询快,修改快,但增删慢。(这些都是相对而言的)
* 底层:链表 --LinkedList
* 查询慢,修改慢,但增删快。
解析:
--集合中的 底层:数组结构 是怎样的一个流程??
* 因为部分集合底层结构是由 数组 来实现的,所以会在底层创建一个数组为10的初始长度,如果要存储的数据超过了这个数组初始长度时即11个时,会再创建一个比原来大50%的数组长度,将原数组中的数据copy到这个新数组当中,将原数组扔掉...以此类推。 因为数组一旦创建,长度就不能被改变,所以随着存储数据的个数越多,数组的长度不够用,就要创建更多的新数组,这样会导致废弃的旧数组越来越多成为垃圾,而占用内存空间。
* 底层结构:数组
* 什么是数组?就是一个容器,里面规定了初始容量是多少的一个器皿。比如初始容量就是一个可以存储10个元素的容器,这就是数组,数组一旦创建就不可被修改。
* 查询快,修改快。是因为数组有索引,根据索引可以方便的 查找 和 修改 对应的数据。
* 增删慢。因为都要对索引对应的数据进行操作,所以慢,如下:。
* 增加慢,如:在索引2的位置上添加一个元素E,就要将索引2包含2的元素整体往后移动一位。将索引2的元素置出,并放入新添加的E元素。
* 删除慢,如:将索引3的元素删除,就是将3+1的索引位置开始往后的元素往前移动一位,移动的最末尾的那个空出来的索引上的元素置为null即可。
--集合中的 底层:链表结构 是怎样的一个流程??
* 底层结构:链表
* 什么是链表? 比如就是一段段自行车的小链条。 他们的链接就是通过 中间链条记录住前面链条的地址值,和再记录住后面链条的地址值,通过这样的形式将链串联起来,就是链表。
* 前提?链表不像数组是一个容器有规矩的索引编号,增和删 索引都要变化。而链表只是通过记录前后地址值来排列索引而记录数据。
* 增加快。所以如果想添加一个元素,可在任意的链条中间添加即可,但增加的同样要记录住它前后的元素的地址值,从而形成一个新的链表。
* 删除快。如果想删除一个元素,直接删除,删除后的其他两个的前后重新拼接一个新的中间链条即可。
* 查询慢:因为要先判断要查找的元素距离开头近还是末尾近,确定后。再通过Next(下一个)还是previous(前一个)的方式查询,这样对比数组直接通过拿索引找元素肯定会慢些。
* 修改慢:因为要先判断要查找的元素距离开头近还是末尾近,确定后。再通过Next(下一个)还是previous(前一个)的方式查询,查到后再修改。这样对比数组直接通过拿索引找到元素并修改它肯定会慢些。
List集合的三个子类的特点
概述
* ArrayList
* 底层数据结构:数组。因为是数组,所以:查询快,修改快。但增删慢。
* 它是 线程不安全 的,不同步。所以操作速度快,效率高。(最后通过StringBuilder来记录元素)
* Vector
* 底层数据结构:数组。因为是数组,所以:CURD都慢。
* 它是 线程安全 的,同步。所以操作速度慢,效率低。
* LinkedList
* 底层数据结构:链表,因为是链表,所以:查询慢,修改慢,但增删快。
* 它是 线程不安全,不同步,所以执行效率高。
Vector要比ArrayList查询慢,因为Vector是线程安全的。
Vector要比LinkedList增删慢,因为Vectory是数组结构的,LinkedList是链表结构的,链表结构增删快,数组增删慢。
Vector和ArrayList的区别
* Vector是 线程安全,所以执行效率低。
* ArrayList是 线程不安全的,所以执行效率高。
* 共同点:底层都是 数组 实现的。
ArrayList和LinkedList的区别
* ArrayList底层是 数组结构,所以查询快,修改快,增删慢。
* LinkedList底层是 链表结构,所以查询慢,修改慢,增删快。
单列集合Collection下的List有三个子类,我们到底使用谁?
* 查改多,用ArrayList
* 增删多,用LinkedList
* 如果都多,就用ArrayList。
【线程安全,意味着有 锁。】
【面试题】
1、集合的继承体系结构?
总:
集合体系有两大部分: 单列集合Collection 双列集合Map
分:
单列结构Collection 下有 List 和 Set
List 有序 有索引,可存储重复元素。
* List下有三个子类
* ArrayList
* 底层结构:数组。查询快,修改快,增删慢。
* 线程不安全的,执行效率高。
* LinkedList
* 底层结构:链表。查询慢,修改慢,增删快。
* 线程不安全的,执行效率高。
* Vector
* 底层结构:数组,查询快,修改快,增删慢。
* 线程安全的,执行效率低。
Set 无序,无索引,不可存储重复元素。
总:
2、数组和集合区别?
数组,既可以存储基本数据类型,也可存储引用数据类型,当存储基本数据类型存储的是值,存储引用数据类型时存储的是地址值。
而集合,只能存储引用数据类型(对象),当集合存储基本数据类型时,会自动装箱封装为对象。(如:int变成Integer对象, char变成Character对象,boolean变Boolean对象)
数组的长度是固定的,一旦初始化不可更改,不可增长。
集合的长度是可变的,可以根据元素的增长而增长,随着元素的减少而减少 --(由元素个数决定)。
3、如何获取字符串、数组、集合的长度?
* 字符串: "".length();
* 数 组: Length属性;
* 集 合: size()方法;
4、遍历集合有哪几种方式?
List
1) 使用 toArray() 将集合转数组的方式 + 普通for循环。
2) 使用 List中size()和get(int index)的方法for循环获取集合元素。
3) 使用 Iterator迭代器,直接遍历集合获取元素。如果使用普通迭代器Iterator 来一边遍历集合元素,一边添加集合元素。一定会出现:并发修改异常。即:ConcurrentModificationException
4) 使用 List中特有的ListIterator 迭代器,就可以一边遍历集合元素,一边使用ListIterator中特有的add方法添加元素,这样就不会有 并发修改异常 的出现。
Vector
1) 使用 Iterator迭代器、
2) Vector vec = new Vector(); Enumeration e = evc.elements(); 获取枚举的方式。
5、什么时候用数组?什么时候用集合?
查改多,用ArrayList
增删多,用LinkedList
如果都多,用ArrayList
6、来说说你对 ArrayList、LinkedList、Vector的理解?
对象数组 --自定义对象类
概述
* 创建一个自定义类用于存储学生信息,并且获取到每个学生信息 。
使用
Student[] s = new Student[5]; //创建student类,用于存储数组对象。
s[0] = new Student("hah", 2); //第一个Student数组的元素,且每个地址值有所不同
s[1] = new Student("fas", 4); // 数组中存储的元素记录的都是地址值,通过每个地址值找到一个个的new的元素对象。
s[2] = new Student("fasd",34);
s[3] = new Student("we", 43);
s[4] = new Student("ad", 23);
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
注意
* 数组和集合存储 引用数据类型 的时候,存储都是地址值,且每个地址值都有所不同,通过地址值来查找每个元素对象。
* 自定义对象数组中 存储的元素记录的其实都是地址值,即每个元素对象的地址值。 通过每个索引地址值找到每个 new的元素对象。
b.
集合
--由来
* 前提:数组和集合都是容器。
* 因为数组的长度是固定的,当添加的元素超过数组的长度需要对数组进行重新定义,这样太麻烦。 所以Java在jdk1.2的时候为我们提供了集合类。集合:用于存储任意对象,而且长度可以改变,是随着元素的增加而增加,随着元素的减少而减少的,即随着元素个数的变化而变化。
--概述
* 数组和集合都是容器。
* 集合于jdk1.2时被提出,长度可改变,是随着元素个数的变化而变化。
数组和集合区别
* 可以从两方面来说:A存储的类型 和 B存储的长度:
* 区别:
区别1:
* 数组既可以存储基本数据类型,也可存储引用数据类型。基本数据类型存储的是值,而引用数据类型存储的是地址值。
* 集合只能存储引用数据类型(对象)集合也可以存储基本数据类型,但存储的时候会自动装箱变成对象,即int类型的100,存储到集合中就变成了new Integer(100)。
区别2:
* 数组的长度是固定的,不可自动增加。
* 集合的长度是可变的,是根据元素个数的变化而变化。
--数组和集合什么时候使用
* 如果元素个数固定,就用数组。
* 如果元素个数未知,不是固定,就用集合。 --部分集合底层代码还是用数组来实现的,如果超出了数组的范围,就创建一个 大于前者的1.5倍长度的数组。增长后,将原数组变成垃圾,以此类推。
--集合继承体系关系图
-Collection --单列集合根接口
* List -存取有序,和数组相同。有索引,可存储重复元素。
* ArrayList 底层 数组实现。
* LinkedList 底层 链表实现。
* Vector(jdk1.0) 底层 数组实现。 --已被ArrayList替代。 之所以和集合命名规则不同,是集合体系是jdk1.2时候被提出,而jdk1.0的时候已经有了Vector,因为Vector中的功能和集合一样,所以让Vector也实现了List接口并且并入到了集合体系中。
* Set -存取无序,无索引,不可存储重复元素。
* HashSet 底层 Hash算法。 元素无序。
* LinkedHashSet 底层 链表结构,是set集合中唯一一个怎么存怎么取的集合,又因为是HashSet的子类,所以同样保证了元素的唯一性。
* TreeSet 底层 二叉树算法。 元素可以进行排序。
-Map --双列集合根接口
* HashMap 底层 哈希算法。 保证键的唯一性。
* TreeMap 底层 二叉树。 键可以排序。
c.
Collection
--概述
* Collection是所有 单列集合 的根类接口。
* Map是所有 双列集合 的根接口。
* 集合是jdk1.2的时候提出。
* 集合的命名都有一定的规范:底层是怎么实现的,前者单词就是什么,后者是哪个继承体系下的。后者单词就是什么。
如:
ArrayList 底层数组实现,属于List集合下。
LinkedList 底层链表实现,所以List集合下。
--体系结构图:
Collection(单列集合根接口)
* List(有序,有索引,可重复元素)
* ArrayList 数组实现。存取有序,和数组一样。有索引,可存储重复元素。
* LinkedList 链表实现。
* Vector(jdk1.0) 数组实现。之所以和集合命名规则不同,是因为在jdk1.0时候出现,而集合体系是jdk1.2时候出现,已被ArrayList替代。
* Set (无序,无索引,不可重复元素)
* HastSet 哈希算法。
* TreeSet 二叉树算法。
注意:List是一个不可插队的插入对象。
d.
Collection集合的基本功能
概述
* util包下,用时需导包。
* Collection 是接口,用时只能:父类引用指向子类对象,即: Collection c = new ArrayList();
* Collection 单列集合 中的根接口。
* Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素(list),而另一些则不允许(set)。一些collection 是有序的(list),而另一些则是无序的(set)。
* JDK不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。
常用方法
* boolean add(Object obj); 添加对象 可添加任意对象。
* 结果Boolean永远都是true。那什么时候是false? 只有当set集合存储重复元素的时候,返回false。
* 所以一般我们不用返回值类型,自己将对象添加到集合中即可。即:c.add("sfa");
*
* boolean remove(Object obj); 删除对象 可删除任意对象。可以是基本数据类型,到时候会自动装箱成对象。
* void clear(); 清空集合元素
* boolean contains(Object o); 判断是否包含指定元素
* boolean isEmpty(); 判断集合是否为空,为空就返回true,否则为false。
* int size(); 获取集合中的元素个数
注意:1)add如果是List集合,就一直返回true,因为list集合中是可以存储重复元素的。
如果是set集合用add存储重复元素,就会返回false。
2)ArrayList的父类的父类重写了toString方法,所以在打印对象的引用时输出的结果不是Object的 toString 的结果,而是集合特有的:[元素]的形式。
collectionXxx.java使用了未经检查或不安全的操作.
注意:要了解详细信息,请使用 -Xlint:unchecked重新编译.
java编译器认为该程序存在安全隐患
温馨提示:这不是编译失败,所以先不用理会,等 学了泛型你就知道了
toArray() 集合的遍历之集合转数组遍历的功能
集合遍历
* 就是依次获取集合中的每个元素。
* 集合转数组,就可以实现对集合的遍历。
* Object[] toArray(); 集合转数组的方式就可以遍历集合中的每个元素。
代码
//自定义 Bean对象 的遍历方式。
Collection c2 = new ArrayList(); //创建单列集合对象。
c2.add(new Student("哈哈",23)); //等同于 Object o = new Student("哈哈",23);
c2.add(new Student("嘻嘻",25));
c2.add(new Student("么么",332));
c2.add(new Student("啦啦",645));
Object[] array2 = c2.toArray();
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]); //获取集合中所有的元素对象。
// 如果想获取每个元素对象中的Name 或 age,就要向下转型,即Object转成Student对象。现在的array2[i] 就是Ojbect对象。
// (看左边的类型,如果左边是Object对象 就是向上转型,如果左边是Student对象,就是向下转型)
Student st =(Student)array2[i]; //向下转型。将Object的array2转成Student。
System.out.println("姓名:"+st.getName()+" 年龄:"+st.getAge());
}
Collection集合中方法带有All后缀的功能
* boolean addAll(Collection c); 添加集合。
* 如:
* c1集合有对象:abcd c2集合有对象:ef
* c1.addAll(c2); 将c2集合中的每个对象添加c1中,结果为:[a,b,c,d,e,f]
* c1.add(c2); 将c2看做是一个整个对象添加到c1中,结果为:[a,b,c,d,[e,f]]
* boolean removeAll(Collection c); 删除集合或删除集合的元素,删除的是两个集合的交集为true,如果删除的是两个集合没有的交集,就是false。
* boolean containsAll(Collection c); 是否一个集合包含另一个集合的所有元素。如果全包含就为true,否之为false。
* boolean retainAll(Collection c); 取出两个集合之间的交集。
* 注意:如果调用的集合改变就返回true, 如果调用的集合没有改变就返回false。
如:
1) c1集合有:abcd c2集合有 ab c1.retainAll(c2); 取交集c1结果为:ab,c1改变了,所以返回true。
2) c1集合有:abcd c2集合有 z c1.retainAll(c2); 取交集c1结果为:[], 但c1还是改变,所以也返回true。
3) c1集合有:abcd c2集合有 abcdefg c1.retainAll(c2); 取交集c1结果为:abcd,c1没有改变,所以为false。
这就是retainAll的特殊之处,主要是 前者跟后者比较,前者的结果是否改变,如果改变就是true,如果没变就是false。
Collection集合的Iterator迭代器的遍历方式
概述
* 集合用于存储元素,存储的元素如果需要查看,就要使用迭代器的方式。
使用
* 使用 Iterator 的方式来迭代。
* 关键方法:
* hasNext(); 判断是否还有下一个元素,如果有就返回true。
* next(); 返回迭代的下一个元素,即将集合中的元素取出。
* next具有将指针向下移一位的功能。
Collection存储自定义对象并遍历
Collection c = new ArrayList(); // 创建集合对象。父类引用指向子类对象
c.add(new Student("赵白石",43)); //等等于: Object obj = new Student("赵白石",43); 即父类引用指向子类对象,obj不能使用子类特有的属性和行为,即使用只能靠向下转型的方式才能够子类特有的属性和行为。
c.add(new Student("沈星移",33));
c.add(new Student("周莹",40));
c.add(new Student("吴聘",42));
// 方式一:while来做
/* Iterator it = c.iterator();
while(it.hasNext()){
Student st = (Student)it.next(); //向下强转。 将it.next的Iterator的对象向下强转成Student对象。只有向下转型,才能使用子类特有的属性和行为。
String name = st.getName();
int age = st.getAge();
System.out.println("姓名:"+name+".....年龄:"+age);
}*/
//【注意:向下转型,才能使用子类特有的属性和行为】
// 方式二:for循环来做
for (Iterator it = c.iterator(); it.hasNext();) {
Student s = (Student)it.next(); //向下强转。将it.next的Iterator的对象向下强转成Student对象。这样就能获取子类特有的属性和行为。
System.out.println(s.getName()+"..."+s.getAge());
}
}
--迭代器的原理及源码解析
原理
迭代器是对集合进行遍历的,而每个集合内部的存储结构都是不同,所以每个集合的存和取的方式也都不同的。那么就需要在每一个类中都去定义hasNext()方法 和 next()方法,这样做可以方便的判断是否有下一个元素和返回集合中的当前元素。但是这样整个集合体系看起来就会过于臃肿。所以就将迭代器向上抽取出一个接口,然后在每个类的内部去定义自己的迭代方式。 这样做的好处有二:
第一:规定了整个集合体系的遍历方式通过通过hasNext()和next()方法来实现的。第二:代码由底层内部实现,使用者无需关心怎么实现的,会用即可。
迭代器的原理:
迭代器是用来遍历集合。因为集合的底层数据结构不同,就导致集合存取元素的方式不同,如果每个集合类都定义自己的存取方式,这样做是可以的,但是会让整个继承体系很冗余,臃肿。所以我们经过不断的向上抽取,最终定义成了接口。
这样做的好处有两个:
A:更规范。
B:我们不用管底层是怎么实现,只要会用就可以了。
迭代器源码解析
* 1,在eclipse中ctrl + shift + t找到ArrayList类
* 2,ctrl+o查找iterator()方法
* 3,查看返回值类型是new Itr(),说明Itr这个类实现Iterator接口
* 4,查找Itr这个内部类,发现重写了Iterator中的所有抽象方法
Iterator iterator(); 该对象必须依赖具体的集合容器,因为每个集合容器的内部数据结构不同,所以该迭代器对象是在集合容器中进行内部实现的,也就是说iterator()方法在每个集合容器中的实现方式时不同的。
对于调用者来说,具体的实现过程无需知道,只要通过该集合容器获取到该实现的迭代器对象即可,也就是iterator方法。
Iterator接口就是对所有单列集合根接口Collection容器进行元素取出的公共接口。
e.
List集合
概述
* List是一个接口,在util包下,使用的话,只能父类引用指向子类对象,如下:
* List i = new ArrayList(); 但这样做存在弊端,不能使用子类特有的属性和方法。
* 建议:ArrayList al = new ArrayList(); // 这样写。直接new对象即可直接使用子类属性和方法。
* 因为多态当 [参数] 用是比较合适。因为可以更好的扩展代码功能。
* 有序集合,有索引,可存储重复元素。
* ArrayList底层 数组实现。
方法
* void add(int index, E element); 在集合的指定索引位置上添加E对象。E表示泛型表示任意对象。
* 注意:
* 当添加时使用不存在的索引,就会出现:IndexOutOfBoundsException 角标越界异常。
* 添加的索引是: index<=size 并且index>=0 在这个范围内 就都不会出错报异常。
* E remove(int index); 通过集合的索引删除元素,将被删除的元素返回。
* * remove的过程不会将索引自动装箱成对象。
* E get(int index); 获取集合指定索引上的元素对象。
* get(int index)方法可作为for循环变量集合的其中一个条件,配合size()方法便可遍历出具体集合元素对象。
* E set(int index, E element); 设置集合索引上的元素。
* int indexOf(Object o); 返回此列表中第一次出现指定元素的索引位置,如果没有就返回-1
* List subList(int fromIndex. int toIndex); 返回列表中从指定位置开始到指定位置结束之间的部分视图。含头不含尾的。
List集合的遍历
概述
* 通过get() 和 size()方法结合使用遍历。
* 代码
List list = new ArrayList(); //创建集合对象。
list.add(new Student("aa",33)); //添加Student元素对象,等于 Ojbect o = new Student("aa",33)
list.add(new Student("bb",23));
list.add(new Student("cc",13));
//遍历集合,使用size()和get()方法去遍历。
for(int i=0;i<list.size();i++){
Object obj = list.get(i); //获取每个集合元素对象。
sop(obj);
//如果想获取具体Student对象的name和age属性,就要向下强转对象,再获取。因为父类不能使用子类特有的属性和行为。
Student st = (Student)obj; //向下强转对象。
sop(st.getName()+"..."+st.getAge());
}
List集合 --并发修改异常产生的原因及解决方案
* 需求
* 我有一个集合,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。
* 问题出现
* 如果使用Iterator普通迭代器 来遍历集合且同时添加元素对象时,就会出现 并发修改异常,即ConCurrentModificationException。
* 当使用Iterator普通迭代器 来作为遍历的方式时,一边遍历元素对象并且一边添加元素对象,就会出现这样的异常。
* 解决方案
* 使用List集合特有的一种迭代方式:ListIterator。
* ListIterator迭代器可以 一边遍历集合,一边使用ListIterator迭代器可以特有的add方法添加元素(不是ArrayList的add方法),这样才不会有 并发修改异常 的出现。
List集合中特有的迭代器 ListIterator 的了解:
* boolean hasNext(); 正向顺序判断是否还有下一个元素。
* boolean hasPrevious(); 逆向顺序判断是否还有前一个元素。
* Object next(); 正向顺序返回下一个元素。
* next()获取元素并将指针向后移动一次。
* Object previous(); 逆向顺序返回上一个元素。
* previous()获取元素并将指针向前移动一次
注意:必须先有正向遍历,才可以逆向遍历。
Vector(1.0)
概述
* 有序集合,在util包下,是一个类。
* 在jdk1.2出现后的集合体系将Vector并入里面,且用 ArrayList代替这个集合了。
* 底层 数组实现。
* 同样具有 Iterator 迭代器,但该迭代器不是自己的,而是从父类接口List继承下来的。
* 他有自己遍历集合的方式:Enumeration
特有功能
* void addElement(E obj); 在没有并入集合系统时,使用的是这个方法添加元素,当并入以后就是用 add() 去添加元素。
* E elementAt(int index); 在没有并入集合系统时,使用的是这个方法添加元素,当并入以后就是用 nextElement() 去添加元素。
* Enumeration elements(); 获取枚举 等同于 Iterator迭代器。
【遍历集合的几种方式】
list
* 使用 toArray() 集合转数组的方式 + 普通for循环。
* 使用 List中的 size() 和 get(int index) 的方式for循环获取集合元素。
* 使用 Iterator迭代器, 直接遍历集合获取元素,如果普通迭代器 一边遍历集合元素,一边添加元素,这样会一定出现:并发修改异常:ConcurrentModificationException。
* 使用 List集合中特有的:ListIterator迭代器,一边遍历集合元素,一边使用ListIterato特有的add()方法添加(修改)元素的方式,这样就不会出现 并发修改异常。
vector
* Iterator迭代器
* Vector vec = new Vector(); Enumeration ele = vec.elements(); 获取枚举的方式。
f.
数组结构之数组和链表 2017/10/5 0:24:53
概述
* 底层:数组 --ArrayList,Vector
* 查询快,修改快,但增删慢。(这些都是相对而言的)
* 底层:链表 --LinkedList
* 查询慢,修改慢,但增删快。
解析:
--集合中的 底层:数组结构 是怎样的一个流程??
* 因为部分集合底层结构是由 数组 来实现的,所以会在底层创建一个数组为10的初始长度,如果要存储的数据超过了这个数组初始长度时即11个时,会再创建一个比原来大50%的数组长度,将原数组中的数据copy到这个新数组当中,将原数组扔掉...以此类推。 因为数组一旦创建,长度就不能被改变,所以随着存储数据的个数越多,数组的长度不够用,就要创建更多的新数组,这样会导致废弃的旧数组越来越多成为垃圾,而占用内存空间。
* 底层结构:数组
* 什么是数组?就是一个容器,里面规定了初始容量是多少的一个器皿。比如初始容量就是一个可以存储10个元素的容器,这就是数组,数组一旦创建就不可被修改。
* 查询快,修改快。是因为数组有索引,根据索引可以方便的 查找 和 修改 对应的数据。
* 增删慢。因为都要对索引对应的数据进行操作,所以慢,如下:。
* 增加慢,如:在索引2的位置上添加一个元素E,就要将索引2包含2的元素整体往后移动一位。将索引2的元素置出,并放入新添加的E元素。
* 删除慢,如:将索引3的元素删除,就是将3+1的索引位置开始往后的元素往前移动一位,移动的最末尾的那个空出来的索引上的元素置为null即可。
--集合中的 底层:链表结构 是怎样的一个流程??
* 底层结构:链表
* 什么是链表? 比如就是一段段自行车的小链条。 他们的链接就是通过 中间链条记录住前面链条的地址值,和再记录住后面链条的地址值,通过这样的形式将链串联起来,就是链表。
* 前提?链表不像数组是一个容器有规矩的索引编号,增和删 索引都要变化。而链表只是通过记录前后地址值来排列索引而记录数据。
* 增加快。所以如果想添加一个元素,可在任意的链条中间添加即可,但增加的同样要记录住它前后的元素的地址值,从而形成一个新的链表。
* 删除快。如果想删除一个元素,直接删除,删除后的其他两个的前后重新拼接一个新的中间链条即可。
* 查询慢:因为要先判断要查找的元素距离开头近还是末尾近,确定后。再通过Next(下一个)还是previous(前一个)的方式查询,这样对比数组直接通过拿索引找元素肯定会慢些。
* 修改慢:因为要先判断要查找的元素距离开头近还是末尾近,确定后。再通过Next(下一个)还是previous(前一个)的方式查询,查到后再修改。这样对比数组直接通过拿索引找到元素并修改它肯定会慢些。
List集合的三个子类的特点
概述
* ArrayList
* 底层数据结构:数组。因为是数组,所以:查询快,修改快。但增删慢。
* 它是 线程不安全 的,不同步。所以操作速度快,效率高。(最后通过StringBuilder来记录元素)
* Vector
* 底层数据结构:数组。因为是数组,所以:CURD都慢。
* 它是 线程安全 的,同步。所以操作速度慢,效率低。
* LinkedList
* 底层数据结构:链表,因为是链表,所以:查询慢,修改慢,但增删快。
* 它是 线程不安全,不同步,所以执行效率高。
Vector要比ArrayList查询慢,因为Vector是线程安全的。
Vector要比LinkedList增删慢,因为Vectory是数组结构的,LinkedList是链表结构的,链表结构增删快,数组增删慢。
Vector和ArrayList的区别
* Vector是 线程安全,所以执行效率低。
* ArrayList是 线程不安全的,所以执行效率高。
* 共同点:底层都是 数组 实现的。
ArrayList和LinkedList的区别
* ArrayList底层是 数组结构,所以查询快,修改快,增删慢。
* LinkedList底层是 链表结构,所以查询慢,修改慢,增删快。
单列集合Collection下的List有三个子类,我们到底使用谁?
* 查改多,用ArrayList
* 增删多,用LinkedList
* 如果都多,就用ArrayList。
【线程安全,意味着有 锁。】
【面试题】
1、集合的继承体系结构?
总:
集合体系有两大部分: 单列集合Collection 双列集合Map
分:
单列结构Collection 下有 List 和 Set
List 有序 有索引,可存储重复元素。
* List下有三个子类
* ArrayList
* 底层结构:数组。查询快,修改快,增删慢。
* 线程不安全的,执行效率高。
* LinkedList
* 底层结构:链表。查询慢,修改慢,增删快。
* 线程不安全的,执行效率高。
* Vector
* 底层结构:数组,查询快,修改快,增删慢。
* 线程安全的,执行效率低。
Set 无序,无索引,不可存储重复元素。
总:
2、数组和集合区别?
数组,既可以存储基本数据类型,也可存储引用数据类型,当存储基本数据类型存储的是值,存储引用数据类型时存储的是地址值。
而集合,只能存储引用数据类型(对象),当集合存储基本数据类型时,会自动装箱封装为对象。(如:int变成Integer对象, char变成Character对象,boolean变Boolean对象)
数组的长度是固定的,一旦初始化不可更改,不可增长。
集合的长度是可变的,可以根据元素的增长而增长,随着元素的减少而减少 --(由元素个数决定)。
3、如何获取字符串、数组、集合的长度?
* 字符串: "".length();
* 数 组: Length属性;
* 集 合: size()方法;
4、遍历集合有哪几种方式?
List
1) 使用 toArray() 将集合转数组的方式 + 普通for循环。
2) 使用 List中size()和get(int index)的方法for循环获取集合元素。
3) 使用 Iterator迭代器,直接遍历集合获取元素。如果使用普通迭代器Iterator 来一边遍历集合元素,一边添加集合元素。一定会出现:并发修改异常。即:ConcurrentModificationException
4) 使用 List中特有的ListIterator 迭代器,就可以一边遍历集合元素,一边使用ListIterator中特有的add方法添加元素,这样就不会有 并发修改异常 的出现。
Vector
1) 使用 Iterator迭代器、
2) Vector vec = new Vector(); Enumeration e = evc.elements(); 获取枚举的方式。
5、什么时候用数组?什么时候用集合?
查改多,用ArrayList
增删多,用LinkedList
如果都多,用ArrayList
6、来说说你对 ArrayList、LinkedList、Vector的理解?