1.集合框架大致示意图:
2.集合概述:
/*
数据 -封装在-> 对象 -存储在-> 集合
集合类:
1.为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对
多个对象的操作,就对 对象进行存储,集合就是存储对象最常用的
一种方式
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的,只能存储同一类型对象
集合长度是可变的.
数组中可以存储基本数据类型,集合只能存储对象
集合类的特点:
集合只用于存储对象
集合长度是可变的
集合可以存储不同类型的对象
关于数组:
例如:
Person p=new int[3];
p[0]=new Person("zhangsan");
...
为什么有这么多容器?(集合)
因为每一个容器对数据的存储方式都有不同
这个存储方式称为数据结构.
*/
3.Collection中的一些方法:
/*
1.add方法的参数类型是Object,以便于接收任意类型的对象
2.集合中存储的都是对象的地址(引用)
*/
import java.util.*;
class CollectionDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void base_method()
{
ArrayList a1=new ArrayList();
a1.add("java1");
a1.add("java2");
a1.add("java3");
a1.add("java4");
ArrayList a2=new ArrayList();
a2.add("java5");
a2.add("java2");
a2.add("java3");
a2.add("java6");
a1.retainAll(a2);
sop("取交集后a1为: "+a1);//[java2,java3]
sop("取交集后a2为: "+a2);//不变
//a1中存放的是a1∩a2(如果为空集则a1为空)
a1.removeAll(a2)
sop(“a1-a1∩a2: ”+a1);
//a1中存放的是a1-a1∩a2
}
public static void main(String[] args)
{
base_method();
//创建一个集合容器.使用Collection接口的子类(ArrayList)
ArrayList a1=new ArrayList();
//1.添加元素.
a1.add("java1");//添加字符串对象
a1.add("java2");
a1.add("java3");
a1.add("java4");
//2.获取个数,集合长度
sop(a1.size());
//打印集合
sop(a1);//继承了AbstractCollection中的toString()方法
//返回此 collection 的字符串表示形式,
//通过 String.valueOf(Object) 可以将元素转换成字符串。
//并用"[]"括起来
//3.删除元素
a1.remove("java2");
sop(a1);
//a1.clear();//清空集合
//4.判断元素
sop("java3是否存在:"+a1.contains("java3"));
sop("集合是否为空:"+a1.isEmpty());
}
}
4.迭代器(Iterator):
/* 什么是迭代器? 其实就是集合的取出元素的方式. 把元素的取出方式定义在集合的内部(内部类) 这样取出方式就可以直接访问集合内部的元素 那么取出方式就被定义成了内部类. 而每一个容器的数据结构不同, 所以取出的动作细节(代码)也不一样. 但是都有共性内容->判断和取出->进行向上抽取描述 这些内部类都符合一个规则.该规则是Iterator. 如何获取集合的取出对象呢? 通过一个对外提供的方法iterator(); (简单示意图) 该方法是Collection接口继承Java.lang.Iterable接口中的iterator()方法 */ import java.util.*; class CollectionDemo3 { public static void sop(Object obj) { System.out.println(obj); } //获取元素 public static void method_get() { ArrayList a1=new ArrayList(); a1.add("java1"); a1.add("java2"); a1.add("java3"); a1.add("java4"); Iterator it=a1.iterator();//返回一个Iterator接口的子类对象 while(it.hasNext()) sop(it.next()); //另一种写法:迭代器对象随着for循环结束变成垃圾,节约内存. for(Iterator it2=a1.iterator();it2.hasNext(); ) sop(it2.next()); } public static void main(String[] args) { method_get(); } }
5.List集合中的方法:
/* Collection<-List:元素是有序的(存入和取出顺序一致,因为有角标的缘故),元素可以重复.因为该集合体系有索引. <-Set:元素是无序的(存入顺序和取出顺序不一定一致),元素不可以重复. List集合特有方法: 凡是可以操作角标的方法都是该体系特有的方法 增 boolean add(index,element) boolean addAll(index,Collection) 删 remove(index) 改 set(index,element) 查 get(index) sublist(fromIndex,toIndex) listIterator() indexOf(Object o) List集合特有的迭代器: ListIterator是Iterator的子接口:可以实现在遍历过程中的对集合的增 删 改 查 当对一个集合在迭代时,不可以通过集合对象的方法操作(增,删)该集合中的元素. 因为会发生ConcurrentModificationException(并发修改异常)异常. 所以,在迭代时,只能用迭代器的方法操作元素, 可是Iterator的方法是有限的, 只能对元素进行判断,取出,删除的操作 如果想要其他的操作:添加,修改等,就需要使用其子接口:ListIterator 该接口只能通过List集合的listIterator方法获取 */ import java.util.*; class ListDemo { public static void sop(Object obj) { System.out.println(obj); } //增 public static void method_add(ArrayList a1) { a1.add(1,"java增"); sop("插入后: "+a1); } //删 public static void method_remove(ArrayList a1) { a1.remove(2); sop("删后: "+a1); } //改 public static void method_set(ArrayList a1) { a1.set(2,"java改"); sop("改后: "+a1); } //查 public static void method_get(ArrayList a1) { sop("get(1): "+a1.get(1)); //取出全部 for(Iterator it=a1.iterator();it.hasNext();) sop(it.next()); sop("\n"); //或通过遍历 for(int i=0;i<a1.size();++i) sop(a1.get(i)); //通过indexOf获取对象位置 sop("indexOf(2): "+a1.indexOf("java改")); sop("subList(1,3): "+a1.subList(1,3)); } public static void main(String[] args) { ArrayList a1=new ArrayList(); a1.add("java1"); a1.add("java2"); a1.add("java3"); a1.add("java4"); sop("原集合为: "+a1); method_listIterator(a1); method_add(a1); method_remove(a1); method_set(a1); method_get(a1); } //ListIterator public static void method_listIterator(ArrayList a1) { //在迭代过程中准备添加或者删除元素 /* for(Iterator it=a1.iterator();it.hasNext();) { Object obj=it.next(); if(obj.equals("java2")) //a1.add("java7");//集合在该位置添加,对迭代器来说还是认为有4个元素,不能确定取不取 //如果用集合进行删除操作,同理. //it.remove();//迭代器删除的是集合java2的引用 sop("obj="+obj); } */ /* 会报并发修改(迭代器和集合操作)异常. 解决: 要么使用集合方法修改, 要么使用迭代器方法修改. */ ListIterator li=a1.listIterator(); //sop("nextIndex: "+li.nextIndex());//0 //sop("previousIndex: "+li.previousIndex());//-1 while(li.hasNext()) { Object obj=li.next(); if(obj.equals("java2")) li.add("java7"); //li.set("java8");//改 } sop("li.add(\"java7\"): "+a1); //sop("nextIndex: "+li.nextIndex());//5 //sop("previousIndex: "+li.previousIndex());//4 //逆向遍历 while(li.hasPrevious()) sop(li.previous()); //sop("nextIndex: "+li.nextIndex());//0 //sop("previousIndex: "+li.previousIndex());//-1 } } /* hasNext(),next()与hasPrevious(),previous public boolean hasNext() { return cursor!=size; } public boolean hasPrevious() { return cursor!=0; } cursor=0; 0 1 2 next() 0 1 2 3 //返回当前元素(return cursor),指向下一个(cursor++)[0,1 1,2 2,3] previous() 0 1 2 //返回前一个(return cursor-1),指向前一个(cursor)[] public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } */
6.ArrayList与Vector
/*
List<-ArrayList:底层的数据结构使用的是数组结构
:查询更改很快,但是增删稍慢
:线程不同步
(线性表...)
<-LinkedList:底层的数据结构使用的是链表结构
(单链表,双向链表,循环链表...)
:增删速度很快,查询稍慢,线程不同步
<-Vector:底层是数组数据结构.
:线程同步.被ArrayList替代了.
可变长度数组:
ArrayList:
public ArrayList()构造一个初始容量为 10 的空列表。
>10 new一个新的数组长度为10*(1+0.5)=15
把原来数组中的元素拷贝到新数组,把新元素添加到末尾元素后.
Vector:则每次需要增大容量时,容量将增大一倍->比较浪费空间
*/
7.Vector集合简单示例:
import java.util.*;
/*
枚举就是Vector特有的取出方式.
发现枚举(Enumeration<E>)和迭代器很像
其实枚举和迭代器是一样的.
因为枚举的名称以及方法名称都过长
所以被迭代器取代了
枚举郁郁而终了
*/
class VectorDemo
{
public static void main(String[] args)
{
Vector v=new Vector();
v.add("java1");
v.add("java2");
v.add("java3");
v.add("java4");
Enumeration en=v.elements();//返回Enumeration<E>是一个接口
while(en.hasMoreElements())//使用的Enumeration中的方法
{
System.out.println(en.nextElement());
}
}
}
8.LinkedList集合:
import java.util.*;
/*
LinkedList特有方法:
addFirst();//头插法建
addLast();//尾插法建表
getFirst();
getLast();
获取元素,但不删除元素
removeFirst();
removeLast();
既取还删,并且返回被删元素
如果列表为空抛出NoSuchElementException
在JDK1.6出现替代方法
offerFirst();
offerLast();
插入指定元素
peekFirst();
peekLast();
获取但不移除此列表的元素;如果此列表为空,则返回 null。
pollFirst()
pollLast();
获取并移除此列表的元素;如果此列表为空,则返回 null。
*/
class LinkedListDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
LinkedList link=new LinkedList();
link.addFirst("java1");
link.addFirst("java2");
link.addLast("java3");
link.addFirst("java4");
sop(link);
sop(link.removeFirst());
sop("size="+link.size());
sop("\n");
while(!link.isEmpty())
sop(link.removeFirst());//取一个删一个
}
}
9.利用LinkedList集合模拟栈和队列:
/*
使用LinkedList模拟一个堆栈或者队列数据结构
堆栈:后进先出 如同一个杯子
队列:先进先出 如同一个水管
*/
import java.util.*;
/*
为什么要封装?
LinkedList只有自身含义
要做成跟项目相关的名称
用自定义名称显得更清晰
*/
class QueueList
{
private LinkedList link;
QueueList()
{
link=new LinkedList();
}
public void queueAdd(Object obj)
{
link.addFirst(obj);
}
public Object queueGet()
{
return link.removeLast();
//return link.removeFirst();栈
}
public boolean isNull()
{
return link.isEmpty();
}
}
class LinkedListTest
{
public static void main(String[] args)
{
QueueList q=new QueueList();
q.queueAdd("aa");
q.queueAdd("cc");
q.queueAdd("ee");
q.queueAdd("ff");
while(!q.isNull())
System.out.println(q.queueGet());
}
}
/*
比较频繁增删操作:LinkedList
增删同时有查询:ArrayList
*/
10.ArrayList集合:去除集合中重复元素
1.存储字符串对象:
/* 去除ArrayList集合中重复的元素 */ /* 算法思想: ①新建一个容器 ②把旧容器中的元素放到新容器中,并且每次放入均进行判断 如果出现相同元素则不再放入. */ import java.util.*; class ArrayListTest { public static ArrayList removeRepeat(ArrayList al) { //方法一:集合操作 /* ArrayList as=new ArrayList(); for(int i=0;i<al.size();++i) if(!as.contains(al.get(i))) as.add(al.get(i)); */ //方法二:迭代器操作 ArrayList as=new ArrayList(); Object obj; ListIterator li=al.listIterator(); while(li.hasNext()) if(!as.contains(obj=li.next())) as.add(obj); return as; } public static void main(String[] args) { ArrayList al=new ArrayList(); al.add("aa"); al.add("bb"); al.add("aa"); al.add("aa"); al.add("cc"); al.add("bb"); System.out.println(al); System.out.println(removeRepeat(al)); } } /* 总结: ①尽量不要next和previous同时用 这样很容易被指针的指向搞糊涂. 它们两个都会改变cursor. ②尽量不要使用多个next,取一次判断一次 */
2.存储自定义对象:
/* 将自定义对象作为元素存到ArrayList集合中,并去除重复元素 比如:存人对象.同姓名同年龄,视为同一个人,为同一个人. */ /* 思想: ①定义人 类,将数据封装人对象.并且复写掉Object中的equals方法 ②调用contains进行比较,筛选出不同的元素 */ import java.util.*; class Person { private String name; private int age; Person(String name,int age) { this.name=name; this.age=age; } public boolean equals(Object obj) { if(!(obj instanceof Person)) return false;//不同的对象 Person p=(Person)obj; return (this.name==p.name)&&(this.age==p.age); //this.name.equals(p.name) 用String类的equals方法 } public void printPerson() { System.out.println("name:"+name+",age:"+age); } //以上也可以通过定义两个方法(getName,getAge)获取姓名和年龄 } class ArrayListTest2 { public static ArrayList singlePerson(ArrayList ai) { ArrayList as=new ArrayList(); ListIterator lt = ai.listIterator(); while(lt.hasNext()) { Object obj; if(!as.contains(obj=lt.next())) as.add(obj); } return as; } public static void main(String[] args) { ArrayList ai=new ArrayList(); ai.add(new Person("zhang",12)); ai.add(new Person("li",12)); ai.add(new Person("wang",13)); ai.add(new Person("zhang",12)); ai.add(new Person("zhang",12)); ai.add(new Person("wang",13)); for(int i=0;i<ai.size();++i) ((Person)(ai.get(i))).printPerson(); System.out.println(); ai=singlePerson(ai); for(int i=0;i<ai.size();++i) ((Person)(ai.get(i))).printPerson(); } } /* contains 方法: //ArrayList类 public boolean contains(Object o) { return indexOf(o) >= 0; } public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i]))//在集合中,AbstractList中复写了equals //其实比较的是集合中对象的地址 return i; } return -1; } public boolean equals(Object obj) { if(!(obj instanceof Person)) return false;//不同的对象 Person p=(Person)obj; System.out.println(this.name+"..."+p.age); return (this.name==p.name)&&(this.age==p.age); //this.name.equals(p.name) 用String类的equals方法 //用这种方法更好点,因为字符串常量也是一个对象 //用==也可以,因为比较的是对象中的name } remove方法也是调用equals方法把要删除的对象与集合中的对象逐一比较 */ /* List集合判断元素是否相同,依据的是equals方法. */
11.HashSet集合:
/*
set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复.
<-hashSet:底层的数据结构是哈希表
Set集合的功能和Collection是一致的
*/
import java.util.*;
class HashSetDemo
{
public static void main(String[] args)
{
String s1=new String("ab");
String s2=new String("ab");
HashSet hs=new HashSet();
hs.add(new HashSetDemo());
hs.add(new HashSetDemo());
hs.add(new String("java"));
hs.add(new String("java"));
System.out.println(hs);
System.out.println(s1.hashCode()+" "+s2.hashCode());// 内存地址不同
//但是其哈希地址相同,String复写了hashCode
/*
在hs集合中由哈希函数决定在哈希表中的存储顺序,
造成存的顺序和取的顺序不一致
而List集合中的元素存的顺序和取的顺序一致
因为每一个元素都有一个索引(角标)
*/
}
}
12.HashSet集合如何保证元素唯一性?
/* 用HashSet存储自定义对象(以人为例) HashSet是如何保证元素唯一性呢? 是通过元素的两个方法,hashCode和equals来完成 如果元素的HashCode值相同,会再调用equals与其具有相同hashCode的元素逐一比较,如果为true则认为相同元素,反之不同元素. 如果元素的hashCode()值不同,不在调用equals进行比较,即说明是不同元素. */ /* 通常自定义对象时,要复写equals和hashCode 因为可能存到HashSet集合中 */ import java.util.*; class Person { private String name; private int age; Person(String name,int age) { this.name=name; this.age=age; } //复写hashCode(),使其返回一个定值 /* public int hashCode() { System.out.println(this.name+"....hashCode()...."); return 60; } */ //重新复写hashCode() public int hashCode()//注意返回值为int,注意复写时返回值不要超出int范围 { System.out.println(this.name+"....hashCode()...."); return name.hashCode()+age*37;//如果不让age*37,不同对象可能出现40+20和30+30,相同概率很高,为了尽量保证hashCode唯一 //利用String类中的hashCode()方法 } public boolean equals(Object obj) { if(!(obj instanceof Person)) return false;//不同的对象 Person p=(Person)obj; System.out.println(this.name+"...equals..."+p.name); return (this.name==p.name)&&(this.age==p.age); //this.name.equals(p.name) 用String类的equals方法 } public void printPerson() { System.out.println("name:"+name+",age:"+age); } } class HashSetDemo2 { public static void main(String[] args) { HashSet hs=new HashSet(); hs.add(new Person("zhang",11)); hs.add(new Person("li",11)); hs.add(new Person("zhang",11)); hs.add(new Person("wang",12)); Iterator it=hs.iterator(); while(it.hasNext()) ((Person)it.next()).printPerson(); } } /*以下为分析过程: 1.未复写hashCode()和equals 会打印出四个值,并且有相同的. 这是因为在存入hs集合时,会首先调用hashCode方法,比较哈希值,即是Object的hashCode方法 Object中的hashCode有个特点: 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数. 也就是说以上为四个不同对象->hashCode值一定不同->因此都存入hs中 2.只复写了equals 在对象存入hs中的时候,并没有调用equals,这是因为hashCode已经不同,无需调用 3.当再把hashCode()复写返回一个定值 ①new Person("zhang",11)会首先调用hashCode,此时集合中没有元素,存入集合 ②new Person("li",11)先调用hashCode->与zhang,11相同 ->调用equals方法与zhang,11比较->return false->存入 ③new Person("zhang",11)调用hashCode->与zhang,11和li,11都相同 ->调用equals方法和zhang,11比较->return true->不再比较并且不存入hs ④new Person("wang",12)同理与以上已存入集合的两个比较->最终存入hs 这时候,发现避免了重复的存入,但是让hashCode返回一个值,这样每个对象都必须进行 两次比较->没有必要(内存地址不同,无需再equals) 4.重新复写hashCode() */ /* 了解下String类中的hashCode() private int hash; public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } */3.
4.
一个非常好的总结关于Equals和HashCode:http://www.iteye.com/topic/257191
13.HashSet中的remove和contains方法:
import java.util.*; class Person { private String name; private int age; Person(String name,int age) { this.name=name; this.age=age; } public int hashCode()//注意返回值为int,注意复写时返回值不要超出int范围 { System.out.println(this.name+"....hashCode()...."); return name.hashCode()+age*37; } public boolean equals(Object obj) { if(!(obj instanceof Person)) return false;//不同的对象 Person p=(Person)obj; System.out.println(this.name+"...equals..."+p.name); return (this.name==p.name)&&(this.age==p.age); //this.name.equals(p.name) 用String类的equals方法 } public void printPerson() { System.out.println("name:"+name+",age:"+age); } } class HashSetDemo2 { public static void main(String[] args) { HashSet hs=new HashSet(); hs.add(new Person("zhang",11)); hs.add(new Person("li",11)); hs.add(new Person("wang",12)); Iterator it=hs.iterator(); while(it.hasNext()) ((Person)it.next()).printPerson(); System.out.println(hs.contains(new Person("zhang",11)));//true,remove同理 //以上,hashCode()与集合中zhang,11相同->在调用equals->依然相同->存在(true) /* 注意: 对于判断元素是否存在,以及删除等操作,依据的是元素的hashCode方法和equals方法 而List集合判断元素是否相同,以及删除等操作,依据的是equals方法. */ } }
来源:https://www.cnblogs.com/yiqiu2324/archive/2013/04/01/2994085.html












