博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【java】详解集合
阅读量:5030 次
发布时间:2019-06-12

本文共 14135 字,大约阅读时间需要 47 分钟。

目录结构:

contents structure
[-]

一,集合概述

1.1什么是集合

集合是对一组存储数据类的统称,相关的类都存放在java.util包中。

Collection:

Map:

图中灰底的类是比较常用的类,从上面的图片中我们可以看出,

集合分为两大类:Collection和Map

1.2 Collection和Map的区别

 Collection接口中存放的是单个元素

Map接口存放的是单对元素

1.3 List和Set的区别

 List接口是有序的,其中的元素可以重复

Set接口是无序的,其中的元素不可以重复

1.4 ArrayList和LinkedList的区别

 ArrayList的底层实现是动态数组结构的,查找和修改元素方便,增加和删除元素不方便。

LinkedList的底层实现是链表结构,增加和删除元素方便,查找和修改元素不方便。

1.5 HashSet和TreeSet的区别

 HashSet的底层是基于HashCode表进行存储的。

TreeSet的底层是基于平衡有序二叉树(又称)实现的。

1.6 HashMap和TreeMap的区别

 HashMap,TreeMap的底层和Set接口中HashSet,TreeSet底层实现结构类似。Set中只能存放单个元素,Map中只能存储单对元素。

1.7 List,Set,Map的比较

List接口是有序的,存储的是单个元素,元素允许重复。

Set接口是无序的,存储的是单个元素,元素不允许重复。

Map接口是采用(key)键-(value)值进行存储,其中key不允许重复,value允许重复。

二,List接口及其常用实现类

List中的元素是有序的。实现List接口中的常用子类有:ArrayList,LinkedList,Stack,Vector

2.1 ArrayList类

由于数组是内存中一段连续的存储空间,因此可以非常方便地通过下标来访问和修改元素。如果在数组的开始和末尾增加或删除元素还比较容易,但是在数组中间增加或是删除某个元素,那么就需要移动其它的元素位置,若数组长度非常大,那么移动的元素就非常多,效率就比较低。

 由于ArrayList的底层实现和数组类似,下面通过一个简单的Demo来看一看:

1         int []arr=new int[10]; 2         /* 3          * 赋值 4          */ 5         for(int i=0;i

上面的代码移除了原数组下标为6的元素,并且移动了数组4次。

ArrayList的底层是采用动态数组实现的,访问和修改方便,增删不方便。

2.2 LinkedList类

附上一张图片来说明LinkedList和ArrayList的区别

LinkedList类的底层是采用动态链表实现的,增删方便,访问和修改不方便。

2.3 Stack类

该类的数据存储结构同栈类似,也是后进先出。

Stack类是采用动态数组的方式实现的,该类是一种具有后进先出特性的数据结构,简称LIFO(Last Input First Output)。

Stack类是Vector类的一个子类,它模拟了“栈”这种数据存储结构,Stack类是一个古老的类,也是线程安全、效率较低的一个类。不建议使用Stack类,如果需要“栈”这种结构可以考虑使用ArrayDeque代替。

2.4 Vector类

该类是采用动态数组的方式实现的,与ArrayList类相比,支持线程安全,效率比较低,Java官方推荐使用ArrayList。

到这里都知道Vector和ArrayList都是List的实现类,在上面关于ArrayList的介绍中,我们已经知道了ArrayList其实是基于动态数组结构的,其实Vector和ArrayList类似,也是基于动态数组。ArrayList和Vector对象是采用initialCapacity参数来设置数组的长度,当ArrayList对象和Vector对象添加的元素超过了数组的长度,initialCapacity会自动增加。如果在创建ArrayList和Vector对象的时候不指定initialCapacity的值,默认的长度是10。我们已经知道官方推荐使用ArrayList代替Vector,那么如何解决ArrayList不是线程安全的问题呢?其实JDK官方提供一个Collections的工具类,可以使用该类实现ArrayList的线程安全,比如: ArrayList arrayList= Collections.synchronizedList(new ArrayList(...));  

2.5 其它

2.5.1 在List集合中存储自定义数据

在List集合中的数据是按照数组结构存储的,因此如果在List集合中存储自定义类数据的时候,不需要在自定义类中继承或是实现某些特殊的接口。

Student类:

1 public class Student { 2      3     private String name;//姓名 4     private int age;//年龄 5      6     public Student() { 7         super(); 8     } 9     10     public Student(String name, int age) {11         super();12         setName(name);13         setAge(age);14     }15 16     public String getName() {17         return name;18     }19     20     public void setName(String name) {21         this.name = name;22     }23     24     public int getAge() {25         return age;26     }27     28     public void setAge(int age) {29         this.age = age;30     }31 }
Student类

TestStudent类:

public class TestStudent{    public static void main(String[] args) {        /*         * 创建一个只能存放Student对象的实现List接口的集合         */        List
lt=new ArrayList
(); /* * 增加数据 */ lt.add(new Student("jame",2001)); lt.add(new Student("john",2002)); lt.add(new Student("Locy",2003)); /* * 打印数据 */ for(Student stu:lt){ System.out.println(stu.getName()+","+stu.getAge()); } }}
TestStudent类

上面的代码中Student类并没有继承或是实现某些特殊的类或是接口,依然能够存放到ArrayList集合中,这和ArrayList的底层的数据存储结构是有关的。因为ArrayList对每个元素都有唯一的索引下标,只需要把元素放到指定的下标位置即可,而无需Student类去实现或是继承某些特殊的接口或是类。

2.5.2 互调List集合中的两个数据

互调两个数据,笔者在脑海中最开始闪现出来的算法如下:

temp = i;i=j;j=temp;

但是我们可以使用List集合中set和get方法,其中set(int index,E element)方法的返回值比较特别:

public E set(int index,E element)功能:将此列表中指定位置的元素替换为指定的元素返回值:以前在指定位置的元素

set方法的返回值是被替换掉的元素,我们可以通过这个特点,来快速地实现List接口实现类集合中两个元素的互调。

list.set(i, list.set(j, list.get(i)));

上面的模板实现了互调下标为i的元素和下标为j的元素,下面以LinkedList为例:

1 public class TestStudent{ 2  3     public static void main(String[] args) { 4         /* 5          * 创建一个只能存放Student对象的实现List接口的集合 6          */ 7         List
lt=new LinkedList
(); 8 /* 9 * 增加数据10 */11 lt.add(new Student("jame",2001));12 lt.add(new Student("john",2002));13 lt.add(new Student("Locy",2003));14 /*15 * 打印数据16 */17 for(Student stu:lt){18 System.out.println(stu.getName()+","+stu.getAge());19 }20 /*21 * 互调学生Locy和jame在List集合中位置22 */23 lt.set(0, lt.set(2, lt.get(0)));24 /*25 * 再次打印26 */27 System.out.println("-------------------------");28 for(Student stu:lt){29 System.out.println(stu.getName()+","+stu.getAge());30 }31 }32 33 }
实现互调List集合中两个元素的位置

最后读者需要注意,使用List和Map接口的实现类可以使用这种方式快速互调两个元素,但是有set接口因为本身存储数据的结构问题,并没有提供这样的方法。

三,Queue接口及其常用实现类

队列(Queue)是常用的数据存储结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一段访问(OFFER)元素,从另一端取出(POLL)元素。

队列(Queue)遵循先进先出(First Input First Output 简称FIFO)的原则。

Queue接口下面有一个Deque接口,Deque接口是具有双端队列的特性的接口,Deque接口的实现类是既可以作为模拟队列这种数据结构,又可以作为模拟栈的数据结构。Deque的实现类常见有ArrayDequ和LinkedList,

ArrayDeque的底层是基于动态数组的,在创建ArrayDeque的时候可以通过numElements参数指定数组的长度,如果不指定,那么默认大小是16.

3.1 LinkedList类

LinkedList是一个比较特殊类,LinkedList和ArrayDeque都同时实现了Deque接口,实现了Deque接口的实体类既可作为栈使用,也可以作为单/双端队列使用。不过LinkedList不仅仅实现了Deque接口,还实现了List接口,也就是说LinkedList类具有三种特性,栈、单/双端队列、List特性。

在前面已经介绍了LinkedList底层是基于链表结构,所以具有插入、删除数据方便,查询数据不方便的特点。

四,Set接口及其常用实现类

Set中的元素是无序的,不允许有重复的元素,而且set和list都是Collection的子接口因此都只能存储单个元素。主要的实现类包括:HashSet()和TreeSet()方法

4.1 HashSet

附上一张关于哈希表的图片

这个里hashCode分配的长度是11,上面的数据对11取余恰好对应其前面的数字。假设目前需要32,那么首先用33对11取余得到0,然后再用33调用equals方法和上面的22,11比是否相等,很显然33不等于22,11,所以33会被分配到11的后面。

HashSet的底层是采用哈希值判断数据在哪个篮子里,然后再用equals方法进行比较看该数据是否存在。

4.1.1 在HashSet中存储自定义数据

在HashSet中存储自定义数据和List接口下的ArrayList(或是LinkedList)不同,因为HashSet的底层是使用HashCode结合equals方法进行管理,因此被存储的数据类必须重写equals和hashCode方法:

下面看一个示例:

 

1 public class Student { 2  3     private String name; 4     private int id; 5     public Student() { 6         super(); 7     } 8     public Student(String name, int id) { 9         super();10         setName(name);11         setId(id);12     }13     14     public String getName() {15         return name;16     }17     public void setName(String name) {18         this.name = name;19     }20     public int getId() {21         return id;22     }23     public void setId(int id) {24         this.id = id;25     }26     /*27      * 该类继承Object,重写其中的hashCode()和equals()方法,按照学号进行比较28      */29     @Override30     public int hashCode() {31         final int prime = 31;32         int result = 1;33         result = prime * result + id;34         return result;35     }36     @Override37     public boolean equals(Object obj) {38         if (this == obj)39             return true;40         if (obj == null)41             return false;42         if (getClass() != obj.getClass())43             return false;44         Student other = (Student) obj;45         if (id != other.id)46             return false;47         return true;48     }49 }
Student类

 

接下来是一个测试类:

 

1 public class TestHashSet { 2  3     public static void main(String []args){ 4         /* 5          * 创建一个只能存储Student对象的HashSet对象 6          */ 7         HashSet
hst=new HashSet
(); 8 hst.add(new Student("jame",23)); 9 hst.add(new Student("jame",23));10 /*11 * 打印所有数据,结果只有一个数据,这是因为Set集合不允许存在重复元素12 */13 for(Student st:hst){14 System.out.println(st);15 }16 }17 }
TestHashSet

 

学生对象的存储过程和上面的列子类似,当我们存储第一个Student对象的时候,会得到一个HashCode值,通过计算可得出是54。然后在储存第二个对象的时候又会得到一个HashCode值,通过计算也可以得出是54,换句话说,这两个对象在同一个“篮子”里,然后该对象再调用equals(Object obj)方法和“篮子”里以前的对象比较是否相等,如果相等就不存入,如果不相等就存入,显然笔者传入的两个对象是相等的,所以最终只有一个数据。

 

4.2 TreeSet

TreeSet的底层是基于平衡二叉树(又称为)的,

由于TreeSet的底层是采用二叉树管理的,被TreeSet存储的元素必须实现java.lang.Comparable接口并且重写compareTo()方法,或在创建集合时传入java.util.Comparator对象并且重写compare()方法

4.2.1 自然排序法

自然排序法就是实现java.lang.Comparable接口,重写compareTo()方法,这是API中大部分类都是实现的,读者可以打开API验证,

 

1 public class Student implements Comparable
{ 2 3 private String name; 4 private int id; 5 public Student() { 6 super(); 7 } 8 public Student(String name, int id) { 9 super();10 setName(name);11 setId(id);12 }13 14 public String getName() {15 return name;16 }17 public void setName(String name) {18 this.name = name;19 }20 public int getId() {21 return id;22 }23 public void setId(int id) {24 this.id = id;25 }26 /*27 * 重写Comparable接口中的compareTo方法28 * 按照id进行比较29 */30 @Override31 public int compareTo(Student o) {32 return getId()-o.getId();33 }34 }
Student
1 public class TestTreeSet { 2  3     public static void main(String[] args) { 4         Set
st=new TreeSet
(); 5 6 st.add(new Student("johe",21)); 7 st.add(new Student("jake",35)); 8 st.add(new Student("brave",12)); 9 10 for(Student s:st){11 System.out.println(s.getId()+","+s.getName());12 }13 }14 15 }
TestTreeSet(自然排序法)

4.2.2 比较器

比较器就是在创建集合时传入java.util.Comparator对象,重写compare()方法,其实现原理和自然排序法类似。

 

1 public class Student { 2  3     private String name; 4     private int id; 5     public Student() { 6         super(); 7     } 8     public Student(String name, int id) { 9         super();10         setName(name);11         setId(id);12     }13     14     public String getName() {15         return name;16     }17     public void setName(String name) {18         this.name = name;19     }20     public int getId() {21         return id;22     }23     public void setId(int id) {24         this.id = id;25     }26 }
Student

 

1 public class TestTreeSet { 2  3     public static void main(String[] args) { 4         Set
st=new TreeSet
(new Comparator
(){ 5 6 /* 7 * 重写Comparator接口总compare()方法 8 */ 9 @Override10 public int compare(Student o1, Student o2) {11 return o1.getName().compareTo(o2.getName());12 }13 });14 st.add(new Student("johe",21));15 st.add(new Student("jake",35));16 st.add(new Student("brave",12));17 18 for(Student s:st){19 System.out.println(s.getId()+","+s.getName());20 }21 }22 }
TestTreeSet(选择器)

 

自然排序法可以重复利用,比较器只能使用一次。

4.3 Set集合中迭代器

对Set集合中的数据的遍历、修改和有点不同,List集合既可以调用add或是get等等方法来实现,也可以创建迭代器(Iterator)来实现,但是Set集合是通过迭代器来实现的。

如:

Set
st=new HashSet
(); st.add("abc"); st.add("ab"); st.add("ac"); //创建迭代器 Iterator
it=st.iterator(); while(it.hasNext()){ System.out.print(it.next()+" ");//abc ac ab }

五,Map接口

Map是采用键-值进行数据存储的。相比读者肯定知道Map集合和Set集合非常相似,其实Set集合的实现就是基于Map集合。在Set集合的源码中,将Set集合存储的值放到Map集合的Key上,然后将Value值设置为一个无意义的Object对象,这样就显示了Set集合。

5.1 TreeMap,HashMap

TreesMap的底层实现和TreeSet类似

HashMap的底层实现和HashSet类似

5.2 在Map中互调两个数据

由于Map集合中的“ V put(K key, V value) ”方法和List集合中的“ E set(int index, E element) ”功能类似,并且put方法的返回值是以前与key关联的值。

map.put(i,map.put(j,map.get(i)));

看一个实例:

1 public class TestMap { 2  3     public static void main(String[] args) { 4         /* 5          * create a Map value 6          */ 7         Map
mp=new HashMap
(); 8 /* 9 * put value10 */11 mp.put(1, "a");12 mp.put(2, "b");13 mp.put(3, "c");14 /*15 * print mp value16 */17 System.out.println("before:"+mp);18 /*19 * exchange two value from one to another20 */21 int i=1;22 int j=3;23 mp.put(i,mp.put(j,mp.get(i)));24 /*25 * print mp value26 */27 System.out.println("after:"+mp);28 29 }30 31 }
TestMap

5.3 Map转化其它集合的方法

Map中提供了三种方法将Map集合转化为其它集合的方法,分别是 Set<Map.Entry<K,V>> entrySet() ,  Set<K> keySet()  ,  Collection<V> values()  方法

5.3.1 keySet()方法的使用

1 public class TestEntrySet { 2  3     public static void main(String[] args) { 4         /* 5          * 创建一个将Integer和String关联起来的Map对象 6          */ 7         Map
hp=new TreeMap
(); 8 9 hp.put(1, "a");10 hp.put(2, "b");11 /*12 * 获得Set视图13 */14 Set
set=hp.keySet();15 /*16 * 使用增加版For循环打印17 */18 for(Integer i:set){19 System.out.println(hp.get(i));20 }21 /*22 * 使用迭代器再次打印23 */24 Iterator
it=set.iterator();25 while(it.hasNext()){26 System.out.println(hp.get(it.next()));27 }28 }29 30 }
使用keySet()获得Set视图

5.3.2 entrySet()方法的使用

1 public class TestEntrySet { 2  3     public static void main(String[] args) { 4         /* 5          * 创建一个将Integer和String关联起来的Map对象 6          */ 7         Map
hp=new TreeMap
(); 8 9 hp.put(1, "a");10 hp.put(2, "b");11 /*12 * Map.Entry
对象13 * 其中有getKey(),getValue(),setValue()等等操作方法14 */15 Set
> set=hp.entrySet();16 /*17 * 使用增加版For循环打印18 */19 for(Map.Entry
i:set){20 System.out.println(i.getKey()+"="+i.getValue());21 }22 }23 24 }
使用entrySet方法获得Map.Entry<k,v>

5.3.3 values()方法的使用

1 public class values{ 2  3     public static void main(String[] args) { 4         /* 5          * 创建一个将Integer和String关联起来的Map对象 6          */ 7         Map
hp=new TreeMap
(); 8 9 hp.put(1, "a");10 hp.put(2, "b");11 12 /*13 * 使用values()方法,获得只能是键-值对中的值,也就是所有key对应value的一个集合14 */15 Collection
c=hp.values();16 /*17 * 使用增加版For循环打印18 */19 for(String i:c){20 System.out.println(i);21 }22 }23 24 }
获得键-值对的值集合

 

转载于:https://www.cnblogs.com/HDK2016/p/6826973.html

你可能感兴趣的文章
在使用webView播放flash或视频文件时无法关闭声音的问题
查看>>
redhat 7 源码安装 mysql5.5.49
查看>>
CCP浅谈
查看>>
NAT虚拟网络配置
查看>>
c#部分---需要实例化的内容;
查看>>
销售类
查看>>
技术项目,问题
查看>>
线程池总结
查看>>
Learning to rank (software, datasets)
查看>>
git常见问题
查看>>
.NETFramework:template
查看>>
HM16.0之帧内模式——xCheckRDCostIntra()函数
查看>>
Jmeter性能测试 入门
查看>>
安卓动画有哪几种?他们的区别?
查看>>
Nodejs学习总结 -Express入门(一)
查看>>
web前端优化
查看>>
ssh 连接原理及ssh-keygen
查看>>
vs2013编译qt程序后中文出现乱码
查看>>
【转】IOS数据库操作SQLite3使用详解
查看>>
Android官方技术文档翻译——ApplicationId 与 PackageName
查看>>