本文主要介绍JAVA中常见容器间的关系和主要区别。JAVA中的容器种类很多,且各有特点。为此特意进行学习研究,写下此文,作为一点总结。若有错误,欢迎拍砖。
上图是JAVA常见的各个容器的继承关系,我们就顺着继承关系说一下各个接口或者类的特点吧。
Iterable 接口
- Iterable是一个超级接口,被Collection所继承。它只有一个方法: Iterator<T> iterator() //即返回一个迭代器
- 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为**“轻量级”**对象,因为创建它的代价小。
- Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。 (2) 使用next()获得序列中的下一个元素。 (3) 使用hasNext()检查序列中是否还有元素。 (4) 使用remove()将迭代器新返回的元素删除。
- Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。 举一个例子来说明迭代器的用法:
public static void main(String args[]) {
List<String> l = new ArrayList<String>();
l.add("aa");
l.add("bb");
l.add("cc");
Iterator iter = l.iterator();
while(iter.hasNext()){
System.out.println((String)iter.next());
}
// for循环的版本
// for(Iterator<String> iter=l.iterator();iter.hasNext();){
// String str = (String)iter.next();
// System.out.println(str);
// }
}
Jetbrains全家桶1年46,售后保障稳定
运行结果:
很明显,iterator用于while循环更方便简洁一些。
Collection 接口
我们直接打开API文档进行查看
文档中写道,JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。也就是一般不会直接使用Collection,而是会使用它的子类,如List或Set。
在图中我标注了4点,不同的Collection子类对于有序性、重复性、null、线程同步都有不同的策略。基于此,Collection的介绍就这样,下面就其具体的子类来进行介绍。
List 接口
- List是有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
- 用户插入的顺序或者指定的位置就是元素插入的位置。它与Set不同,List允许插入重复的值。
- List 接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator 接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法(如下)来获取从列表中指定位置开始的列表迭代器。
ListIterator <E> listIterator(int index) 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始
- List 接口提供了两种搜索指定对象的方法。从性能的观点来看,应该小心使用这些方法。在很多实现中,它们将执行高开销的线性搜索。
- List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法。
- List的子类
- List最流行的实现类有Vector、ArrayList、LinkedList。三者的区别将在下文提及。
- 1.1) ArrayList (类)
- ArrayLis是基于数组实现的List类,它封装了一个动态的、增长的、允许再分配的Object[ ]数组.它允许对元素进行快速随机访问
- 当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
- 1.2)Vector (类)
- Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。所以现在已经不太常用了 1.2.1)Stack (类) Stack是Vector提供的一个子类,用于模拟”栈”这种数据结构(LIFO后进先出)
- 1.3)LinkedList (类)
- LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。
- 另外,它还实现了Deque接口,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
关于ArrayList和Vector的区别,再次回顾一下: 名称 扩容方式(默认) 线程安全 速度 有效个数的属性 ArrayList 增长50% 线程不安全 快 size Vector 增长一倍 线程安全 慢 elementCount 共同点 如果新增的有效元素个数超过数组本身的长度,都会导致数组进行扩容 – remove,add(index,obj)方法都会导致内部数组进行数据拷贝的操作,这样在大数据量时,可能会影响效率 –
写在后面:List接口的排序可以通过Collections.sort()来进行定制排序。 只需要继承Comparable接口后,重写compareTo()**方法。 代码如下:
代码语言:javascript复制 public class Student extends Thread implements Comparable {
private int age;
private String name;
private String tel;
private int height;
public Student( String name,int age, String tel, int height) {
this.age = age;
this.name = name;
this.tel = tel;
this.height = height;
}
public static void main(String args[]) throws InterruptedException{
Student stu1 = new Student("张三",21,"156482",172);
Student stu2 = new Student("李四",18,"561618",168);
Student stu3 = new Student("王五",19,"841681",170);
Student stu4 = new Student("赵七",20,"562595",180);
List<Student> list = new ArrayList<Student>();
//乱序插入
list.add(stu3);
list.add(stu1);
list.add(stu4);
list.add(stu2);
System.out.println("-----------排序前----------");
Iterator<Student> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
/*
* 使用Collections的sort方法让集合排序,使用其方法必须要重写
* Comparable接口的compareTo()方法
* */
Collections.sort(list);
System.out.println("-----------按照年龄排序后----------");
for(int i=0;i<list.size();i ){
System.out.println(list.get(i).toString());
}
}
//重写compareTo方法,用age来比较。也可以用别的来比较
@Override
public int compareTo(Object o) {
//使用当前对象的年龄和其他对象的年龄比较,如果<0返回负数,>0返回正数,=0返回0
int z = this.age - ((Student)o).getAge();
if(z<0)
return -1; // 返回其他负数也行
else if(z == 0)
return 0;
else
return 1; //返回其他正数也行
}
//重写toString,便于输出
@Override
public String toString(){
return name "," age "," tel "," height;
}
输出结果如下:
Set接口
- Set,顾名思义,集合的意思。java的集合和数学的集合一样,满足集合的无序性,确定性,单一性。所以可以很好的理解,Set是无序、不可重复的。同时,如果有多个null,则不满足单一性了,所以Set只能有一个null。
- Set类似于一个罐子,丢进Set的元素没有先后的差别。
- Set判断两个对象相同不是使用”==”运算符,而是根据equals方法。也就是说,我们在加入一个新元素的时候,如果这个新元素对象和Set中已有对象进行注意equals比较都返回false,则Set就会接受这个新元素对象,否则拒绝。
——因为Set的这个制约,在使用Set集合的时候,应该注意两点:
- 为Set集合里的元素的实现类实现一个有效的equals(Object)方法;
- 对Set的构造函数,传入的Collection参数不能包含重复的元素。
- Set的子类
- Set最流行的实现类有HashSet、TreeSet、LinkedHashSet(从HashSet继承而来)。
- 1.1) HashSet (类)
- HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的 hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。
- 值得主要的是,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法的返回值相等
。
1.1.1)LinkedHashSet(类)
- LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。
- 当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。
- LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)
- 1.2) SortedSet (接口):
此接口主要用于排序操作,实现了此接口的子类都属于排序的子类
- 1.2.1)TreeSet(类)
- TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
- 1.3) EnumSet (类)
- EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,
Queue 接口
此接口用于模拟“队列”数据结构(FIFO)。新插入的元素放在队尾,队头存放着保存时间最长的元素。
- Queue的子类、子接口
- 1.1) PriorityQueue—— 优先队列(类)
- 其实它并没有按照插入的顺序来存放元素,而是按照队列中某个属性的大小来排列的。故而叫优先队列。
- 1.2) Deque——双端队列(接口)
- 1.2.1)ArrayDeque(类) 基于数组的双端队列,类似于ArrayList有一个Object[] 数组。
- 1.2.2)LinkedList (类)(上文已有,略)
简单回顾一下上述三个接口的区别
容器名 | 是否有序 | 是否可重复 | null的个数 |
---|---|---|---|
List | 有序 | 可重复 | 允许多个null |
Set | 无序 | 不可重复 | 只允许一个null |
Queue | 有序(FIFO) | 可重复 | 通常不允许插入null |
Map 接口
- Map不是collection的子接口或者实现类。Map是一个接口。
- Map用于保存具有“映射关系”的数据。每个Entry都持有键-值两个对象。其中,Value可能重复,但是Key不允许重复(和Set类似)。
- Map可以有多个Value为null,但是只能有一个Key为null。
- Map的子类、子接口
- 1) HashMap (类)
和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准一样: 两个key通过equals()方法比较返回true、 同时两个key的hashCode值也必须相等
- 1.1) LinkedHashMap (类)
- LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序区分)。
- 2) HashTable (类)
是一个古老的Map实现类。
- 2.1) Properties(类)
- Properties对象在处理属性文件时特别方便(windows平台的.ini文件)。Properties类可以把Map对象和属性文件关联,从而把Map对象的key – value对写入到属性文件中,也可把属性文件中的“属性名-属性值”加载进Map对象中。
- 3) SortedMap(接口)
如同Set->SortedSet->TreeSet一样,Map也有Map->SortedMap->TreeMap的继承关系。
- 3.1) TreeMap(类)
- TreeMap是一个红黑树结构,每个键值对都作为红黑树的一个节点。TreeMap存储键值对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。 同时,TreeMap也有两种排序方式:自然排序、定制排序(类似于上面List的重写CompareTo()方法)。
- 4) WeakHashMap(类)
- 看名字就能发现,这是Weak后的HashMap。但是二者大体差别不大。
- 区别在于,HashMap的key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被垃圾回收。
- 但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃圾回收了该key所对应的实际对象之后,WeakHashMap也可能自动删除这些key所对应的key-value对。
- 5) IdentityHashMap(类)
- 这个类也和HashMap类似(怎么那么多类似的hhhh),区别在于,在IdentityHashMap中,当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等
- 6) EnumMap(类)
- EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的自然顺序存储。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/222881.html原文链接:https://javaforall.cn