Collection
集合框架:
集合是一个容器,数组buffer都是容器,对象也是容器
10 9 2 4 1 4 2想要操作怎么办?
1、8个变量弄一弄,面对8个变量很麻烦,但是麻烦装在是实体里很简单
2、装在是实体里很简单 int[] arr ,但是装的是同类型
3、"zhangsan",true,67 数据不同类型,个数可能也不能确定 String Builder,但是有局限性,必须转换成字符串才可以使用,如果想取里面的zhangsan难,几乎取不出来 zhangsantrue67
4、"李四" 23 还需要单独取出来,对事物进行描述,都是事物的属性值,封装到对象 new Person("李四",23) new个对象,堆空间创建了对象,如果这样对象一多,堆内存就可能很多这样人对象,
5、数值有很多数组来存,数组有很多,二维数组存;数据很多,对象存,对象很多集合存,集合专门用于存对象,集合本身就是容器,本身也是对象,存对象的对象,集合里面存的地址,地址怎么排放不知道,所以容器有很多,有各自的特点。
6、容器为什么来,因为面向对象的语言会产生很多对象,为了方便操作对象,所以要对对象进行存储,集合是用于存储对象的容器。
7、数组也可以存Person,为什么存容器?因为对象的个数不能确定,是变化的,数组是固定长度,容器是可变长度。
总结:
1、对象用于封装特有数据,对象多了需要存储,如果对象个数不确定,就是用集合容器就行存储。
2、集合特点(也可以说是和数组的区别)
1)用于存储对象的容器
2)集合的长度是可变的
3)集合中不可以存储基本数据类型值(数组可以存储基本数据类型)
对象多了,不断往一个容器里存储的时候,就会出现一个小问题,对象如何在集合中进行存储的?
容器不只是一种,对对象的存储方式都有自己独特的定义,是针对某些需求而定的。
比如水杯,有些带着茶隔,有些没有茶隔。有茶隔喝茶叶水,就能隔离茶叶。
容器自身都有特定的结构,这个结构称为数据结构。数据按照什么方式存储的,称为数据的结构。
每一个容器的结构,内部都不太一样
部分容器带着一部分共性,先部分抽取,再不断向上抽取,形成了集合框架体系,里面内容有很多,如此庞大并不可怕。
看顶层,用底层。顶层定义集合体系中的共性内容,想使用集合体系,搞清楚这个都能使用。Collection
如何判断容器具有什么方法?按照面向对象的思考,容器有什么功能方便我的指挥?装东西,删东西,取东西。。
学习集合体系,就不在lang包,核心包,现在用的是util包中 Collection接口,在不断向上抽取的时候发现全抽象了,跟接口,表示一组对象
集合容器因为内部数据结构不同,有多种具体容器,不断向上抽取,就形成了集合框架,框架的顶层就是Collect,里面定义集合的共性方法,添加删除。。
目前就把E当做Object。我有一容器,我要放Person,接受的也就是Person对象,我又Demo对象,就要Demo,也就是我们要接受任意对象Object,先学习集合的使用,后学习新特性
Collection的常见方法:必要掌握
1.添加
boolean add(E e)
boolean addAll(Collection<? extends E> c)
2.删除
boolean remove(Object o)
boolean removeAll(Collection<?> c)
void clear()
3.判断
boolean contains(Object o)
boolean containsAll(Collection<?> c)
boolean isEmpty()
4.获取
int size()
Iterator<E> iterator() 取出集合中元素的方法 迭代器
4.其他
bolean removeAll(Collection<?> c) 取交集
Object[] toArray() 将集合转换成数组
迭代器基本原理:
1、 Collection Iterator iterator() 取出元素来用的,但是每个元素怎么取他不知道,即怎么实现时依赖子类实现的
2、 容器不同,取的东西也不一样,都有自己的数据接口,要依据具体的容器来取其具体的东西,意味着取出的对象要与容器相关联,他在直接访问容器内部的内容
3、容器存储元素用的,最终要取出来,取出的是一个对象。对象是对容器中的元素直接访问,这个对象就应该是在该容器内部完成的实现
4、容器中有很多元素,有自己的排放规则,现在要取出来,即迭代器,实现依赖于容器内部结构,根据容器内部的的结构,创建自己独特的迭代器(取出动作),直接访问容器中元素,迭代器的实现通过内部类来完成
5、一个事物在直接访问另外一事物中的内容,即内部事务,内部类涉及方式
6、每个容器内部都有自己的取出方式,因为取出方式都在容器内部,以为取出对象类型都在内部,外部不知道,为了能建立每一个容器内部统一取出规则,对其进行了规则的定义。就抽出共性内容Iterator,这些容器公共的取出方式,有了接口,我们不用直接面对容器中取出来的容器对象,只要找到迭代器接口,对每一个容器进行取出的动作。如果不这样做找到容器,找到容器对去除对象才能使用。 这样提升了容器和取出方式的耦合性提升了,
7、迭代器就是实现了Iterator接口的每一个容器内部的内部对象。没有必要知道怎么实现,只要获取到接口怎么使用就可以。
8、迭代器,取出元素的对象,该对象必须一类具体的容器,因为每一个容器的数据结构都不同,所以该迭代器对象是在容器中进行内部实现的。
9、对于使用容器而言,具体实现不重要,只要通过容器获取到该实现的迭代器对象即可
10、也就是iterator方法
11、iterator接口就是对所有Collection容器进行元素取出的公共接口,不需要知道各个具体容器
12、一个事物直接访问另外一个事物成员,就是内部类
开发的时候不知道是用List还是Set?(都是Collection 子类)
List
有序的:怎么存进去的,就怎么取出来(存入和取出顺序一致)
元素都有索引(角标)
元素可以重复
Set
元素不能重复
无序(有可能会有序,人家里面有自己存的方式,正好相同)
List特有的常见方法:
1、有一个共性特点就是都可以操作角标
1.添加
void add(int index, E element)
void addAll(Collection<? extends E> c)
2.删除
boolean remove(Object o)
3.修改
E set(int index, E element)
4.获取
E get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
List<E> subList(int fromIndex,int toIndex)
List-常用小对象(开发时应用的都是这些对象 具体的容器你要明确他是什么特点你才好使用它,容器能区分因为数据结构不一样,长度可变的数组)
1- Vector
1- 内部是数组数据结构。每一个元素有编号,连续的存储,一开辟空间就很多小格子出来了
2- 具备list所有特点
3- 同步的 ,效率低
4- 百分之百延长 浪费空间效率
5- 增删查询都很慢,同步就很慢。
2- ArrayList
1- 内部是数组数据结构,是不同步的,效率高。替代了Vector。
2- 如果多线程不用Vector,一般加锁,把添加删除放在同一个锁里面,保证同步
3- 可增长的数组,百分之延长(数组不是不变的,怎么不断往里面存数组?创建新数组,原来元素复制到新数组中来。数组开辟空间是角标,开辟一大块空间)
4- 增删速度慢,一次操作影像全部,查找元素速度很快,即查询速度快。
3- LinkedList
1- 内部是链接列表数据结构,即链表,不是同步,底层不是数组,不连续也有编号
2- 增删元素的速度很快,对元素频繁增删,执行相当快。
3- 为什么查询快呢?
4- 也有角标,list的子类,最大特点有角标
5- 区别
1- addFirst()
addLast()
offerLast() 阅读性更强
2- getFirst() 获取但是不移除,链表没有东西,会抛出异常,没有这个元素
getLast()
peekFirst() 获取但是不移除,链表没有东西,返回null,可以做判断 jdk1.6
3- removeFirst() 获取移除,链表没有东西,会抛出异常,没有这个元素
removeLast()
pollFirst() 获取移除,链表没有东西,返回null,可以做判断 jdk1.6
6- 相对ArrayList少用些,如果真不知道用啥选用ArrayList,但是每一种情况都有更合适的集合
故用容器干嘛?增删LinkedList,查询ArrayList。容器不一样步骤不一样
--------------------------------------------------------------------------------------------------------------
学习策略:先看这个公共内容,顶层。
接口学习完了找子类
Set
1-元素不可以重复,是无序的
2-Set接口中的方法和Collection一致,不用在学了
3-HahSet:
1- 迭代:取出数据 不管你怎么存数据,我取出来的顺序不保证 允许null元素,无序
2- 不同步的
3- 内部数据结构式哈希表,是不同步的
4- 保证唯一的,存重复数据没有用
注意:
1-哈希是一种算法,这种算法算出很多值,这些算法算出来很多值存储起来,就是哈希表。表里面有一一对应关系。哈希表里面其实是一个函数。哈希这种算法对数组进行了优化。
数组查找元素还是需要挨个去比较,查找元素,遍历,挨个查找,慢。折半还需要前提,无序没有用。哈希就是优化查找,性能稳定高效
以前是按照角标挨个存进来,挨个遍历比;现在有一个元素,,如果有个字符串“ab”,ab存放数组,按理说往0角标放,找的时候慢。
但是现在呢,存的时候根据元素的特点,来确定它在数组中的位置。根据元素自身的特点来获取元素的位置。
怎么算出ab的位置呢,定义一个方法,你把元素给我,我这里边是一个算法,对元素进行运算,并获取其位置。
好处:你给我一个ab,我给你算出位置,返回索引。如果你想查找这个元素ab的位置,不需要遍历,直接拿着ab在用算法,寻找位置,是否ab
换言之,你把元素给我,我就告诉你位置,怎么算的呢?核心就是算法,就是哈希算法。
每个对象都有哈希值,每一个对象都是Object子类,都有父类hashcode方法,走了底层,自己的哈希方法,可以覆盖得到你自己定义的哈希值。
function(element){
//一个算法。对元素进行运算,并获取其位置
//自定义哈希算法 ,哈希算法不唯一,根据不同算法获取不同值,值很大取余就可以了,任何运算取模运算,肯定在这个值范围内
97 + 98 = 195
return 195%10;5角标,ab有位置,并根据算法算出来,根据自身特点算出来的
//return index; //ac 97 + 99 %10;6 优势就是找ab
//ab怎么找,丢给算法,返回5,直接找5,5上是不是ab,5上不是ab,只能说明ab不存在,别的就不去找了,表里面绝对不能有重复,如果有ab就会有问题
//如果存了一个ab,还想存一个ab,哈希算出来,角标对应位置已经有一个ab了,位置上已经元素了,判断你的元素和我的元素是否一样,一样的话不存,存的话也会覆盖,多一步没有意义。哈希算法出现提高查询效率,但是不能重复
//判断元素相同的方式是什么?
//存ab已经有ab,首先把ab放里面算地址,发现位置上有值了,只能说名已经有元素了,你能保证存的值都是ab嘛?不能因此判断这两个值是相同的
//ab 和 ba 位置都是一样,但是不相同,所以哈希表里面必须是不重复的,哈希表如何确定元素是重复的
//先看哈希值是否相同,相同然后判断内容,即先判断位置,再判断内容,
//存ad,位置是7,没有任何值,不会引起冲突
//只有当两个值算出来相同的时候,才会继续判断内容,如果算出来不一样,就不用判断内容相同,多余了
//哈希表确定元素是否相同
1- 第一步是判断两个元素的哈希值是否相同
2- 如果相同,再判断两个对象内容是否相同
//判断哈希值相同,其实判断的是对象的哈希code方法
//判断内容相同用的是equals方法。
//注意:如果哈希值不同,是不需要判断equals
//哈希表特殊内容:如果存的ab,地址相同,内容相同不存;内容不相同,就需要存,哈希冲突,对象不一样,一般不常见,因为哈希算法很复杂,但是有的话也有解决方法,而且不唯一
//解决方案:顺延,如果顺延没位置,将数组长度延长;或者串联在5的位置上再挂一个出来,串联也会继续算位置,只不过基于5来算的;本身有自己的规则,用算法来实现
//如果在数组查询有没有ba,首先在算法找位置,先比较ab不一样,顺延找ba,这时候就不存了,即基于5 的内容。内部自己实现的规律,不产生冲突
//即地址相同,内容不同,也会找到指定的位置存储的,哈希冲突造成,不多见,但是有,哈希值数大,重复几率低
//如果使用hash表,存对象,就要注意equals方法和hashcode方法
//所以存字符串的时候,使用的hashcode方法
}
4-TreeSet:
package collocation.demo;
public class Person {
private String name;
private int age;
public Person() {
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", getAge()=" + getAge() + ", hashCode()=" + hashCode()
+ ", getName()=" + getName() + ", getClass()=" + getClass() + ", toString()=" + super.toString() + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package collocation.demo;
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListDemo {
/**
* 通常存储的是自定义的对象
*/
public static void main(String[] args) {
ArrayList a1 = new ArrayList();
a1.add(new Person("lisi1", 19));
a1.add(new Person("lisi2", 29));
a1.add(new Person("lisi3", 39));
a1.add(new Person("lisi4", 49));
Iterator it = a1.iterator();
while (it.hasNext()) {
// System.out.println(it.next());
/**
* 为什么建议强转
* 列表可以存任意类型数据,可以接受任意类型数据,Object接受
* 所有的具体的对象都提升了Object,获取的对象也是Object,person不再是person,是Object,object没有这个方法,所以失败了
*(Person)it.getAge() c错误的 点的优先级很高 把整体转成person了
*((Person) it.next()).getAge()者却应该这样转换
*ctrl+1
*/
// System.out.println(((Person) it.next()).getAge());
/**
* 李四2,4没有了,next自动往下走,操作元素角标,循环里面调用两次,会出错,获取了第一个人的姓名,第2个人的年龄
* 循环里面不能有两个next
*/
Person p = (Person) it.next();
System.out.println(p.getAge()+p.getName());
/*
* 存自定义对象的时候不要忘记取出来的时候做强转,不强转是作为字符串输出,有些对象没有定义toString,用的父类toSring,是全类名+哈希值
*/
/*
* 1.4不行,只能装对象
* 1.5可以 自动装箱,什么时候自动装箱?什么时候拆箱
* 装箱:基本数据类型赋值给了引用数据类型装箱,只要认为类型符合就会装箱
* 拆箱:当引用数据类型和基本类型数据做运算的时候,拆箱
*/
a1.add(5);
show(6);
}
}
private static void show(Integer num) {//Objet num = new Integer(6)
// TODO Auto-generated method stub
int x = num+8;//拆箱
}
}
package collocation.demo;
import java.util.LinkedList;
/**
* 队列,先进先出,怎么存进来就怎么去取出去
* 自己开发容器很困难,就是用已经有的容器
*
*/
public class DuiLie {
private LinkedList link ;
public DuiLie() {
link = new LinkedList();
}
/**
* 队列添加元素的功能
* @param obj
*/
public void myAdd(Object obj) {
link.addLast(obj);
}
/**
* 只要相反队列
* @return
*/
public Object myGet() {
return link.removeFirst();
}
public boolean isNull() {
return link.isEmpty();
}
}
package collocation.demo;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetDemo {
public static void main(String[] args) {
HashSet hs = new HashSet();
String str = null;
hs.add("haha");
hs.add("xixi");
hs.add("heihei");
hs.add("heihei");
hs.add("heihei");
hs.add("heihei");//结果heihei只有一个。
hs.add("heihei");
hs.add("hehe");
Iterator it = hs.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
package collocation.demo;
import java.util.HashSet;
import java.util.Iterator;
/**
* 往hashset集合中存储Person对象,如果姓名和年龄相同,视为相同元素。
*
*/
public class HashSetTest {
public static void main(String[] args) {
System.out.println("11");
HashSet hs =new HashSet();
hs.add(new Person("lisi4",24));
hs.add(new Person("lisi7",27));
hs.add(new Person("lisi1",21));
hs.add(new Person("lisi9",29));
hs.add(new Person("lisi7",27));//为啥还是打印的两个lisi7
/**
* HashSet集合数据结构是哈希表,所以存储元素的时候。
* 使用元素的hashcode方法来确定位置,如果位置相同,再通过元素的equals来确定是否相同
* Person继承Object,有哈希值,是调用系统哈希值,因为用的Object哈希方法,equals也是Object方法
*/
Iterator it = hs.iterator();
while (it.hasNext()) {
Person p = (Person)it.next();
System.out.println(p.getAge()+"......"+p.getName());
}
}
}
package collocation.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
*迭代器演示
* 基本使用
*/
public class IteratorDemo {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add("abcd1");
coll.add("abcd2");
coll.add("abcd3");
coll.add("abcd4");
//使用了Collection中的iterator()方法.调用集合中的迭代器方法,为了获取集合中的迭代器对象
Iterator it = coll.iterator();//会自动取下一个,因为有指针,先走第一个,指针再偏向第二个
// System.out.println(it.next());
// System.out.println(it.next());这个是一个大字符串
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());// java.util.NoSuchElementException 超出这个元素了
// //这里是取出每一个进行处理
// while (it.hasNext()) {
// System.out.println(it.next());
// }
// System.out.println(it.next());//it还是可以用,但是没有什么意义,占内存空间了,可以for循环
for (Iterator iterator = coll.iterator(); iterator.hasNext();) { //开发写这个,循环迭代后就不能用了,节省内存
System.out.println( iterator.next());
}
}
}
package collocation.demo;
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList link = new LinkedList();
link.addFirst("abc111");
link.addFirst("abc222");
link.addFirst("abc333");
link.addFirst("abc444");
// //获取第一个但是不删除
// Object first = link.getFirst();
// System.out.println(first);
// Object first1 = link.getFirst();
// System.out.println(first1);
// Iterator it = link.iterator();
// System.out.println(link);
// Object remove = link.remove();
// Object remove1 = link.remove();
// //remove会改变长度的,获取元素但是会删除
// System.out.println(remove+"....."+remove1+"..."+link);
// while (it.hasNext()) {
// System.out.println(it.next());
/**
* 可以去取元素,但是集合里面没有了
* 你可以去取元素,但是集合没有了,如果你需要集合是ok的
*/
while (!link.isEmpty()) {
System.out.println(link.removeFirst()); //存123,取321`
}
// }
}
}
package collocation.demo;
/**
* 请使用linkedList来模拟一个堆栈或者队列数据结构
*
* 堆栈:
* 先进后出 First In Last Out FILO
* 队列:
* 先进先出 First In First Out FIFO
*
* 提供对象,容器,怎么存进来,怎么取出来,封装起来
*
* 我们应该描述一个这样的容器,给使用者提供一个容器对象完成这两种结构中的一种
*
*/
public class LinkedListPractice {
public static void main(String[] args) {
DuiLie lie = new DuiLie();
lie.myAdd("111");
lie.myAdd("222");
lie.myAdd("333");
while (!lie.isNull()) {
System.out.println(lie.myGet());
}
}
}
package collocation.demo;
import java.util.ArrayList;
import java.util.List;
/**
*
*
*/
public class ListDemo {
public static void main(String[] args) {
List list = new ArrayList();
show(list);
}
public static void show(List list) {
//添加元素
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
System.out.println(list);
//插入元元素
list.add(1, "abc9");
System.out.println(list);
//删除元素
System.out.println("remove:"+list.remove(2));
System.out.println(list);//为甚么删除对象,返回真假,删除角标返回对象呢?只有角标,不知道对象,所以返回对象让你看看,删除对象你知道指定的对象
//修改元素 collection不支持修改
System.out.println("set:"+list.set(1, "www"));
System.out.println(list);
//获取元素
System.out.println("get:"+list.get(0));//java.lang.IndexOutOfBoundsException:
// System.out.println("get:"+list.get(4));//java.lang.IndexOutOfBoundsException:
//获取子列表
System.out.println("sublist:"+list.subList(1, 3));
/*
* 操作角标的都是方法
* list集合是可以完成对元素的增删改查
* */
}
}
package collocation.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* List取得操作
*/
public class ListDemo2 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
/**
* list特有的取出方式,Set不具备
*/
System.out.println("get:"+list.get(0));
for (int i = 0; i < list.size(); i++) {
System.out.println("get:"+list.get(i));
}
}
}
package collocation.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo3 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
System.out.println("原列表是:"+list);
//他可以实现在迭代过程中完成对元素的增删改查
//注意只有list接口具备该迭代功能
ListIterator it = list.listIterator();//获取列表迭代器对象
while (it.hasPrevious()) {//逆序取
Object obj= it.next();
if (obj.equals("abc2")) {
it.set("abc9");
}
}
/* while (it.hasNext()) {
Object obj= it.next();
if (obj.equals("abc2")) {
it.set("abc9");
}
}*/
// System.out.println("next"+it.hasNext());//下一个
// System.out.println("previous"+it.previous());//上个
System.out.println("新列表是:"+list);
/*Iterator it = list.iterator();
//所以这段代码不能使用
while (it.hasNext()) {
// System.out.println(it.next());
Object obj = it.next();
if (obj.equals("abc2")) {
list.add("abc9");//迭代器在操作这个元素,你又用集合在操作这个元素,迭代器不知道你的操作,集合迭代器同时修改,就会迭代出问题,使用迭代不用集合,使用集合不用迭代
//迭代器只能判断,迭代下个元素,删除元素,但是想修改,可以用迭代器子接口 列表迭代器去实现,列表迭代器怎么拿到呢?
//ListIterator<E> listIterator(int index) 根据这个方法拿到列表迭代器,只有list有
//在迭代过程中不要使用集合操作元素,容易出现异常,并发修改
//怎么解决问题?可以使用Iterator子接口ListIterator来完成,在迭代中多元素进行更多操作
}else {
System.out.println(it.next());
}
}
System.out.println(list);*/
}
}
package collocation.demo;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
Vector v = new Vector();
v.addElement("aa1");
v.addElement("aa2");
v.addElement("aa3");
/**
* 特有的元素
*
*/
Enumeration e = v.elements();
while (e.hasMoreElements()) {
System.out.println( "nextElement"+e.nextElement());
}
//优先考虑 取名简单易懂,for循环靠谱,添加了移除
Iterator i = v.iterator();
while (i.hasNext()) {
System.out.println("next"+i.next());
}
}
}