集合也是Java中的一种类,其作用和数组类似,都是用来存放数据的,不过底层结构和存放方式都有很大不同
数组:被创建时长度固定,后续不可进行改变;
集合:创建时未指定长度,根据使用情况动态改变长度;
数组:可以存放基本数据类型,也可存放引用类型;类型在创建时固定;
集合:只能存放引用类型数据;存放类型为Object类型;
数组length()只能判断长度,不可知其中实际存有多少元素;集合可通过size()得到元素个数;
数组存放元素只能通过顺序方式;集合根据底层结构,可以有更多选择;
集合以类的形式存在,可以进行更多的复杂操作
特有方法:(继承此接口所有子类的共有方法)
- .add() ——添加一个元素
- .addAll() ——添加一组元素
- .clear() ——清空集合
- .remove() ——移除一个元素
- .removeAll() ——移除一组元素
- .contains() ——判断此元素是否存在
- .isEmpty() ——判断集合是否为空
- .size() ——获取集合中的元素个数
ArrayList li =new ArrayList();
li.add("遗憾怎么了");
li.add("遗憾怎么了了");
li.add("遗憾怎么了了了");
li.add("遗憾怎么了了了了");
li.add("遗憾怎么了了了了了");
//hasNext()判断集合下一个元素是否存在
for(Iterator it = li.iterator();it.hasNext();)
{
System.out.println(it.next());
}
List集合可以对角标进行操作,因此具有一些特有方法
- add(index,element) ——指定插入
- remove(index) ——指定删除
- set(index,element) ——指定修改
- get(index) ——角标查询
- subList(1,3) ——截取子串(含头不含尾,截取1,2)
//常用遍历集合元素的方法
for(int i = 0; i<arr.size();i++){
sop("arr["+i+"]:"+arr.get(i));
}
因Iterator在使用中局限性太大,迭代时不能使用更改容器元素的指令,因此拓展了ListIterator的接口,用于在迭代中对元素进行操作
ArrayList arr = new ArrayList();
arr.add("wsf_001");
arr.add("wsf_002");
arr.add("wsf_003");
arr.add("wsf_004");
sop(arr);
for(Iterator it = arr.iterator();it.hasNext();){
//Iterator迭代器无法在迭代时对集合进行更改
Object obj = it.next(); //迭代器和容器方法不能并发
if(obj.equals("wsf_003")){
arr.add("wsf_0035");
}
sop("Iterator: "+obj);
}
for(ListIterator list = arr.listIterator();list.hasNext();){
//可以在迭代中更改集合元素
Object obj = list.next();
if(obj.equals("wsf_003")){
list.add("wsf_0035");//迭代器自身的方法添加元素
}
if(obj.equals("wsf_004")){
list.set("wsf_0039");
}
if(obj.equals("wsf_001")){
list.remove();
}
sop("ListIterator: "+obj);//打印的还是本来的元素,更改的是元素的引用
}
sop(arr);//更改成功
ArrayList的数据结构为数组,创建时初始长度为10,当集合装满后,以当前长度的50%进行延长
- 特点:查询速度快,增删速度慢;线程不同步
- 原因:数据结构为数组,查询时根据索引 寻找,速度快;当增加数据时(中间插入),需要先将插入点之后的数据逐个向后移一位,再进行插入;删除同理,删除后需要将删除点后的数据逐个向前移一位,因此增删的速度很慢;
- 使用:ArrayList建议使用在单线程的环境下,可以用于存放一些以查询居多,不经常进行增删的数据
LinkedList的数据结构为链表结构
- 特点:增删速度快,查询速度慢
- 原因:LinkedList采用的是双向链表结构,当增删节点时,只需要改变前后两个节点的prev、next引用即可;但查询时,由于没有索引,只能通过遍历的方式,效率较低。
- 特有方法:
LinkedList link = new LinkedList();
link.addFirst("wsf_001");//特有方法,首位添加
link.addLast("wsf_004");//末尾添加
link.getFirst();
link.getLast();//取出首尾元素
link.clear();
sop(link);//link为空,抛出异常
//新版本方法特性
LinkedList link1 = new LinkedList();
link1.offerFirst("wsf_001");//替代add
link1.peekFirst();//替代get
link1.peekLast();
while(!link1.isEmpty()){
sop("poll:"+link.pollFirst());//替代remove
}
link1.clear();
sop("link empty:"+link.pollFirst());//新版本不再抛出异常,返回NULL
拓展:链表结构
是List容器下的第三个分支,其大部分功能与ArrayList重合
独有特点:
- 线程同步、安全
- 空间100%增长,不节约空间
- 特有方法:elements()——枚举
Enumeration en = v.elements();
en.hasMoreElements();//判断下一个元素是否为空
en.nextElements();//获取下一个元素
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;不保证该顺序恒久不变。此类允许使用null元素
HashSet是基于HashMap实现的,其储存的对象实际上是由HashMap的Key进行保存,因此其不可重复
元素不可重复的实现:
//重写hashCode方法,使相同对象返回的哈希值是相同的,从而进入比较环节
public int hashCode(){
return name.hashCode()+age*2;
}
public boolean equals(Object obj){
if(!(obj instanceof Ren)){
return false;
}
//向下类型转换
Ren r = (Ren)obj;
//自定义比较方式
return this.name.equals(r.name)&&this.age == r.age;
}
底层数据结构为二叉树,可以对元素进行排序
实现:通过元素比较性或者比较器,对元素进行排序
@Date
class Car implements Comparable{
private String name;
private int price;
public Car(String name,int price){
this.name = name;
this.price = price;
}
@Override
public int compareTo(Object obj) {
//重写比较方法,用价格进行排序
// return 1;//无需判断,怎么存的,怎么取,-1为倒叙,0只留第一个
if(!(obj instanceof Car)){
throw new RuntimeException("不是学生对象");
}
Car c = (Car)obj;
//决定了顺序或者是逆序
if(this.price>c.price)
return 1;
if(this.price==c.price)
{
return this.name.compareTo(c.name);//主要条件排序过后,遇到相等的元素,继续排序次要条件;
}
return -1;
}
}
//比较器方法,新建类实现Comparator接口
class Mycompare implements Comparator{
public int compare(Object o1, Object o2) {
Car c1 = (Car)o1;
Car c2 = (Car)o2;
int num = c1.getName().compareTo(c2.getName());
if(num == 0)//主条件重复,对次要条件进行比较,3种方式
{
// return c1.getPrice()-c2.getPrice(); //方法1,求差,最简便
//方法二,逐个列出条件,最灵活
// if(c1.getPrice()>c2.getPrice())
// return 1;
// if(c1.getPrice()==c2.getPrice())
// return 0;
// return -1;
//方法三,因int对象Integer自身具有比较方法compareTo,因此将int转换为Integer后进行比较
return new Integer(c1.getPrice()).compareTo(new Integer(c2.getPrice()));
}
return num;
}
}
在新建集合时,加入比较器
TreeSet t = new TreeSet(new Mycompare());//加入比较器
比较器特点:使容器自身具备比较性,其优先级高于自然排序
通过存入键值对,来存储对象,其中键的值唯一,不可重复
特有方法:
- put(key,value)——添加(返回值:Value,键重复时,覆盖操作,并返回旧值)
- putAll(Map
)——添加全部 - remove(Key)——删除
- clear()——清空
- containsValue(value)——判断value是否存在
- containKey(key)——判断key是否存在
- isEmpty——判断是否为空
- get(key)——通过key获取值,不存在返回null
- size() ——获取长度
- values()——获取所有的值(没有键)
- keySet()——将map转换为set,利用迭代器取出所有的键,再使用get获取所有值
Map<String,String> map = new HashMap<String,String>();
Set<String> set = map.keySet();
Iterator<String> it = set.iterator();
while(it.hasNext()){
String k = it.next();
String v = map.get(k);
sop("key:"+k+"----value:"+v);
}
- entrySet()——存放键值对的映射关系
通过内部接口Entry存放,并使用其特有方法getKey(),getValue()获取元素
Set<Map.Entry<String, String>> set1 = map.entrySet();
Iterator<Map.Entry<String, String>> it1 = set1.iterator();
while(it1.hasNext()){
Entry<String, String> m1 = it1.next();
String k = m1.getKey();
String v = m1.getValue();
sop("key:"+k+"----value:"+v);
}
HashTable底层为哈希表,功能和HashMap大致相似,有部分差异
- 线程同步:很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低
- 不可插入Null
相比之下,HashMap的效率更快,因此在单线程情况下,已经取代了HashTable;
底层原理与HashSet相同,都是用的是哈希表数据结构,使用链表+数组的储存方式(防止哈希冲突)
- 允许存入NULL
- 线程不同步,用于单线程
- 效率高(链表需要遍历,因此链表越少,效率越高)
- 重复时覆盖
底层原理与HashSet相同,底层为二叉树,用于给Map集合中的Key排序
实现方法与HashSet一致