集合类的由来:
对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定,就使用集合容器进行存储。
集合和数组的区别:
数据结构:由于每种容器对数据的存储方式都各不相同,所以出现了不同的容器,且每种容器都有自己特有的数据结构。
不断的向上抽取,就形成了集合框架。
集合的一些技巧:
需要唯一吗?
需要:Set
需要制定顺序:
需要: TreeSet(指定方式排序)
不需要:HashSet
但是想要一个和存储一致的顺序(有序:怎么存怎么取):LinkedHashSet
不需要:List
需要频繁增删吗?
需要:LinkedList
不需要:ArrayList
如何记录每一个容器的结构和所属体系呢?
看名字!
List
|--ArrayList
|--LinkedList
Set
|--HashSet
|--TreeSet
后缀名就是该集合所属的体系。
前缀名就是该集合的数据结构。
看到array:就要想到数组,就要想到查询快,有角标.
看到link:就要想到链表,就要想到增删快,就要想要 add get remove+frist last的方法
看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法。
看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator 。
而且通常这些常用的集合容器都是不同步的。
框架的顶层Collection接口:
从上图可以看出List和Set的父类是Collection,所以Collection所具备的方法就是他们所共有的共性方法
Collection定义了集合框架的共性功能。
1、添加
add(e);添加一个元素 addAll(collection);添加一组collection中所有元素
2、删除
remove(e);删除一条元素 removeAll(collection);删除一组collection中所有元素 clear();清空集合
3、判断
contains(e);是否包含某个元素
isEmpty();判空
boolean containsAll(Colllection coll);
4,获取:
int size():集合的大小,也就是获取集合中的元素个数。
5,其他:
boolean retainAll(Collection coll);取交集。
Object[] toArray():将集合转成数组。
6. Iterator
iterator():取出元素的方式:迭代器。
该对象必须依赖于具体容器,因为每一个容器的数据结构都不同,所以该迭代器对象是在容器中进行内部实现的。
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可, 也就是iterator方法。
通过迭代的方式获取集合中的每个元素,简单的说就是遍历集合
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
其实就是抓娃娃游戏机中的夹子!
boolean | hasNext() 如果仍有元素可以迭代,则返回 true。 |
---|---|
E | next() 返回迭代的下一个元素。 |
void | remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 |
示例:
lang包核心包不需要导入,其他包需要手动导入,如util工具包
public class CollectionDemo { public static void main(String[] args) { Collection coll = new ArrayList();//子类可随便写,为了使用接口的使用方法 Collection c1 = new ArrayList(); Collection c2 = new ArrayList(); show(c1,c2); } public static void show(Collection c1,Collection c2){ //给c1添加元素 c1.add("abc1"); c1.add("abc2"); c1.add("abc3"); c1.add("abc4"); //给c2添加元素 c2.add("abc1"); c2.add("abc2"); c2.add("abc3"); c2.add("abc4"); c2.add("abc5"); System.out.println("c1:"+c1); System.out.println("c2:"+c2); //演示addAll// c1.addAll(c2);//将c2中的元素添加到c1中 //演示removeAll// boolean b = c1.removeAll(c2);//将两个集合中的相同元素从调用removeAll的集合中删除// System.out.println("removeAll:"+b); //演示containsAll// boolean b = c1.containsAll(c2);// System.out.println("containsAll:"+b); //演示retainAll boolean b = c1.retainAll(c2);//取交集,保留和指定的集合相同的元素,而删除不同的元素 //和removeAll功能相反 System.out.println("retainAll:"+b); System.out.println("c1:"+c1); } public static void show(Collection coll){ //1,添加元素 coll.add("abc1"); coll.add("abc2"); coll.add("abc3"); System.out.println(coll); //2,删除元素// coll.remove("abc2");//会改变集合的长度,当清除的内容不存在时,返回原集合 //清空集合// coll.clear(); System.out.println(coll.contains("abc3")); System.out.println(coll);}public class IteratorDemo { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("abc1"); coll.add("abc2"); coll.add("abc3"); coll.add("abc4");// System.out.println(coll); //使用了Collection中的iterator()方法,调用集合中的迭代器方法,是为了获取集合中的迭代器对象// Iterator it = coll.iterator();// while(it.hasNext()){// System.out.println(it.next());// } for(Iterator it = coll.iterator(); it.hasNext(); ){ System.out.println(it.next()); //for循环的方式在内存中稍优于while,因为for循环中的局部变量随着for循环的消亡而消亡 }// 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 如果这样写没有下一个了就异常了 } }
Collection接口包含的子类:
Collection接口包含最常用的子类是List与Set
List:元素是有序的,元素可重复,因该集合体系有索引
Set:元素是无序的,元素不可重复
迭代器:
因为每种容器的数据结构都不同,所以取出的细节也就不同,所以就把迭代器定义在容器的内部,方便于访问集合内部的元素。然而他们也有共性内容就是判断和取出,就对其进行了抽取就成了Iterator接口,就可以通过Iterator的判断取出方式就可以对元素取出。在迭代时next()最好只调用一次,因为集合中的元素是奇数次的话,就会发生异常,所以具有安全隐患。
注意:1、add方法的参数类型是Object。以便于接收任意类型对象。 2、集合中存储的都是对象的引用(地址)
一、概述:
1、常见的list集合有ArrayList、LinkedList以及Vector等
2、特点:元素是有序的元素可以重复,因为该集合体系有索引。
3、List集合中常见的子类说明:
1)ArrayList:底层数据结构是数组结构。特点,查询速度很快,但增删稍慢,线程不同步。
2)LinkedList:底层使用的是链表数据结构。特点,增删速度很快,查询稍慢,线程不同步。
3)Vector:底层是数组数据结构,线程同步,被ArrayList取代,因为它的效率低。
ArrayList和Vector初始化长度都是10,当超过时,arraylist每次增加5(节约空间),而vector每次增加10
二、List集合特有的方法:
注:凡是可以操作角标的方法都是该体系特有的方法。
增:add(index,element);在指定位置添加元素 addAll(index,Collection);在指定位置上添加一组元素
删:remove(int index)移除指定位置上的元素
改:set(int index,E element)修改指定位置上的元素
查:get(index)获取指定位置上的元素。subList(from,to)获取子list。listIterator其中提供取出数据时对数据操作一些特有方法。indexOf(obj):获取指定元素的位置
注:
1、listIterator是List特有的迭代器,是Iterator子接口。在迭代时,不可通过集合对象的方法操作集合中的元素,因为会发生ConcurrentModficationException异常。所以,在迭代时,只能用迭代器的方法操作,可Iterator方法是有限的,若想要其他操作如增删改写等,就需要使用子接口,即ListIterator,该接口只能通过List集合的listIerator方法获取。
2、在迭代时,循环中的next()调用一次,就要对hasNext判断一次,不可判断一次调用两次。
3、List集合判断元素是否相同,依据的是元素的equals方法,其中,contains和remove中就是调用的equals方法。
class ListDemo { public static void sop(Object obj) { System.out.println(obj); } public static void method() { ArrayList al = new ArrayList(); //添加元素 al.add("java01"); al.add("java02"); al.add("java03"); sop("原集合是:"+al); //在指定位置添加元素 al.add(1,"java09"); //删除指定位置的元素。 //al.remove(2); //修改元素。 //al.set(2,"java007"); //通过角标获取元素。 sop("get(1):"+al.get(1)); sop(al); //获取所有元素:有序的就可以用这样的取值方式,list特有的取出元素的方式之一 for(int x=0; x<al.size(); x++) { System.out.println("al("+x+")="+al.get(x)); } Iterator it = al.iterator(); while(it.hasNext()) { sop("next:"+it.next()); } //通过indexOf获取对象的位置。 sop("index="+al.indexOf("java02")); List sub = al.subList(1,3); sop("sub="+sub); } public static void main(String[] args) { //演示列表迭代器。 ArrayList al = new ArrayList(); //添加元素 al.add("java01"); al.add("java02"); al.add("java03"); //在迭代过程中,准备添加或者删除元素。 Iterator it = al.iterator(); //传入三个,有三个元素,加入一个元素,迭代器不知道加不加 while(it.hasNext()) { Object obj = it.next(); //接受一个元素 //用迭代器取出元素的同时,用集合的方式在操作元素,这样的并发访问有安全隐患,容易抛出异常 if(obj.equals("java02")) //可以使用Iterator接口的子接口ListIterator来完成在迭代中对元素进行更多的操作 //al.add("java008"); it.remove();//将java02的引用从集合中删除了,对象还在内存中,只是集合中没了 sop("obj="+obj); } ListIterator li = al.listIterator(); while(li.hasNext()) { Object obj = li.next(); if(obj.equals("java02")) //li.add("java009"); li.set("java006"); } while(li.hasPrevious()) { sop("pre::"+li.previous()); } //sop("hasNext():"+li.hasNext()); 正序 //sop("hasPrevious():"+li.hasPrevious());逆序 sop(al); sop(al); }
三、ArrayList:
示例:
将自定义对象作为元素存到ArrayList集合中,并去除重复元素,
比如:存人对象,同姓名同年龄,视为同一个人,为重复元素 思路:
1,对人描述,将数据封装进人对象
2,定义容器,将人存入
3,取出
List集合判断元素是否相同,依据是元素的equals方法
class GetSingleObject { public static void main(String[] args) { ArrayList<Per> list = new ArrayList<Per>(); list.add(new Per("zhangsan01", 21));////obj接收,自动提升为obj,person成了上帝,这里报错了?? list.add(new Per("zhangsan02", 22)); list.add(new Per("zhangsan03", 23)); list.add(new Per("zhangsan03", 23)); list.add(new Per("zhangsan04", 24)); System.out.println("remove:" + list.remove(new Per("zhangsan03", 23)));// remove底层也调用了equals进行比较 // hashset中删除或包含一个元素都要判断该元素是否有和该元素中的元素相同,判断依据hashcode和equals // sop(list); // al= getsingleElement(al); // sop(al); // al.add(5);//1.4这样做al.add(new Integer(5)); // 1.5以后集合可以装数据,自动装箱拆箱(当基本数据类型给引用数据类型装箱,当基本数据类型和引用数据类型做运算拆箱) } public static ArrayList getsingleElement(ArrayList al) { // 定义一个临时容器 ArrayList temp = new ArrayList(); // 迭代al集合 Iterator it = al.iterator(); while (it.hasNext()) { Object obj = it.next(); if (!temp.contains(obj)) {// contains底层调用equals方法进行比较,看不懂了 temp.add(obj); } } return temp; } class Per { private String name;// 定义姓名 private int age;// 定义年龄 Per(String name, int age) { this.name = name; this.age = age; } // 重写equals方法,建立Per类自己的比较方式 public boolean equals(Object obj) { if (!(obj instanceof Per)) { throw new RuntimeException("类型错误"); } Per p = (Per) obj; return p.getName().equals(this.name) && p.getAge() == this.age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }}
四、LinkList:
1、特有方法: addFirst(); addLast();
getFirst(); getLast(); 获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst(); removeLast(); 获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException 在JDK1.6出现了替代方法。 offerFirst(); offerLast(); peekFirst(); peekLast(); 获取元素,但不删除元素。如果集合中没有元素,会返回null。 pollFirst(); pollLast(); 获取元素,但是元素被删除。如果集合中没有元素,会返回null。
/* 使用LinkedList模拟一个堆栈或者队列数据结构 堆栈:先进后出 如同一个杯子 队列:先进先出 First in First out FIFO 如同一个水管 */ public class LinkedTest { public static void main(String[] args) { Duilie dl = new Duilie(); dl.myAdd("java001"); dl.myAdd("java002"); dl.myAdd("java003"); dl.myAdd("java004"); while(!dl.isNull()){ System.out.println(dl.myGet()); } } } class Duilie{ private LinkedList<String> link = new LinkedList<String>(); public void myAdd(String str){ link.addFirst(str); } public String myGet(){ return link.removeLast();//removefirst } public boolean isNull(){ return link.isEmpty(); }
五、Vector:
枚举就是Vector特有的取出方式。发现枚举和迭代器很像。其实枚举和迭代是一样的。 因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。枚举郁郁而终了。
带Element都是它特有的属性
public class VectorDemo { public static void main(String[] args) {//枚举 Vector v = new Vector(); v.addElement("abc1"); //比较长 v.addElement("abc2"); v.addElement("abc3"); v.addElement("abc4"); Enumeration en = v.elements(); while(en.hasMoreElements()){ System.out.println("nextelment:"+en.nextElement()); } // 迭代器 Iterator it = v.iterator(); while(it.hasNext()){ System.out.println("next:"+it.next()); } } }
一、概述:
Set集合元素是无序(存入和取出的顺序不一定一致,在哈希表中其实是按照hash值来存放的),元素不可以重复。Set接口中的方法和Collection一致。
二、Set集合常用子类之一HashSet:
1、概述:HashSet底层数据结构是哈希表,线程不同步。
2、HashSet保证元素唯一性:
是通过元素的两个方法,hashCode和equals来完成。如果元素的HashCode值相同,才会判断equals是否为true。如果元素的hashcode值不同,不会调用equals。(先比较哈希值,哈希值一样比较是否是同一个对象,如不是同一个对象就存放在同一位置,两个串起来,放在同一个位置上)
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。(ArrayList只依赖equals)
一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法,建立对象判断是否相同的依据。
if(this.hashCode()== obj.hashCode() && this.equals(obj))
哈希表确定元素是否相同
1,判断的是两个元素的哈希值是否相同。
如果相同,在判断两个对象的内容是否相同。
2,判断哈希值相同,其实判断的是对象的hashCode的方法。判断内容相同,用的是equals方法。
注意:如果哈希值不同,是不需要判断equals。
示例:
用哈希值按顺序取出
public class HashSetDemo { public static void main(String[] args) { HashSe hs = new HashSet(); hs.add(new Course_1("java01", 26)); hs.add(new Course_1("java02", 24)); hs.add(new Course_1("java03", 28)); hs.add(new Course_1("java04", 20)); hs.add(new Course_1("java04", 20)); //obj中的equals方法是比较内容,所以可以存入相同的元素,保证不了唯一,所以在Course_1中复写hscode和equals方法 for (Iterator it = hs.iterator(); it.hasNext();) { Course_1 c = (Course_1) it.next(); System.out.println(c.getName() + "::::" + c.getPrice()); } } } /* * 课程类 */ class Course_1 { private String name; private int price; Course_1(String name, int price) { this.name = name; this.price = price; } public int hashCode() { return this.name.hashCode()(stringname有hashcode方法) + price * 20;// *20为了尽量保证哈希值不同,减少比较equals方法 ,姓名和年龄一起保证哈希值,减少第二次判断 } public boolean equals(Object obj) { //人对象中的equals if(this==obj){ return ture } if (!(obj instanceof Course_1)) { throw new RuntimeException(); } Course_1 c = (Course_1) obj; System.out.println(this.name + "....equals..." + c.getName()); return c.getName().equals(this.name) && c.getPrice() == this.price; //字符串的equals,name就是字符串。当前对象和子对象进行比较 } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }
有序的用linkedhashset
public class HashSetDemo { public static void main(String[] args) { LinkedHashSet<Object> set = new LinkedHashSet<>(); set.add("abc"); set.add("bdc"); set.add("cdb"); set.add("efc"); Iterator<Object> iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next());// abc、bdc、cdb、efc } }}
有序无序不是很重要,不需要唯一list;,需要唯一set,唯一和有序用linkedhashset就可以
三、Set集合常用子类之二TreeSet: