前言
??前面的学习当中我们学过了数组,我们知道了数组的长度是固定的,它不能自动增长,不过在前面也有接触到集合的概念,以及一些集合的应用,当然我们知道集合的长度是可变的,它是可以随着元素的增加而增长的,接下来我们将进行具体讲解和分析一下集合的相关知识点,并给大家提供了经典的案例–斗地主,欢迎大家参考交流!
集合框架
??在学习集合前,首先我们来了解一下集合的框架图: ??从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。 ??集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:
??接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
??实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
??算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
??除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。 集合框架体系如图所示
Collection集合
??通过上面的图我们可以了解到,集合有单列和多列,接下来先了解单列集合Collection集合
Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
Collection集合的基本使用:
public class CollectionDemo01 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("hello");
c.add("world");
c.add("java");
System.out.println(c);
}
}
Collection集合的常用方法:
Collection集合的遍历:
关于Collection集合的遍历,在这里主要介绍一种迭代器的方式:
- 迭代器,集合的专用遍历方式
- Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
- 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
public class IteratorDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("world");
c.add("java");
c.add("javaee");
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
??增强for循环遍历
for(元素数据类型 变量名 : 数组/集合对象名) { 循环体;} |
List
List集合的概述和特点
- List集合概述
- 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
- List集合特点
List集合的特有方法
ArrayList、LinkedList
List集合子类的特点
LinkedList集合的特有功能:
Set
Set集合的概述和特点
- Set集合的特点
- 元素存取无序
- 没有索引、只能通过迭代器或增强for循环遍历
- 不能存储重复元素
- Set集合的基本使用
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("hello");
set.add("world");
set.add("java");
set.add("world");
for(String s : set) {
System.out.println(s);
}
}
}
HashSet
哈希值简介
哈希值是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
- 如何获取哈希值
? Object类中的public int hashCode():返回对象的哈希码值 - 哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
HashSet集合概述和特点
HashSet集合的特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
HashSet集合保证元素唯一性源码分析
HashSet集合保证元素唯一性的原理 :
1.根据对象的哈希值计算存储位置 ????如果当前位置没有元素则直接存入 ????如果当前位置有元素存在,则进入第二步
?2.当前元素的元素和已经存在的元素比较哈希值 ????如果哈希值不同,则将当前元素进行存储 ????如果哈希值相同,则进入第三步
3.通过equals()方法比较两个元素的内容 ????如果内容不相同,则将当前元素进行存储 ????如果内容相同,则不存储当前元素 HashSet集合保证元素唯一性的图解:
LinkedHashSet集合特点
- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素
TreeSet
TreeSet集合的概述和特点
TreeSet集合概述:
- 元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator) :根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以不包含重复元素的集合
自然排序Comparable的使用
- 案例需求
- 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
- 实现步骤
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
学生类
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public int compareTo(Student s) {
int num = this.age - s.age;
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
测试类
public class TreeSetDemo02 {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>();
Student s1 = new Student("xishi", 29);
Student s2 = new Student("wangzhaojun", 28);
Student s3 = new Student("diaochan", 30);
Student s4 = new Student("yangyuhuan", 33);
Student s5 = new Student("linqingxia",33);
Student s6 = new Student("linqingxia",33);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
for (Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
比较器排序Comparator的使用
- 案例需求
- 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
- 实现步骤
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
测试类
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Student s1 = new Student("xishi", 29);
Student s2 = new Student("wangzhaojun", 28);
Student s3 = new Student("diaochan", 30);
Student s4 = new Student("yangyuhuan", 33);
Student s5 = new Student("linqingxia",33);
Student s6 = new Student("linqingxia",33);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
for (Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
Queue
??Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。 ??基本方法:
Map集合
Map集合的概述和特点
Map集合的基本功能
??基本功能: ??获取功能:
Map集合的遍历方式
方式一
- 遍历思路
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 把所有的丈夫给集中起来
- 遍历丈夫的集合,获取到每一个丈夫
- 根据丈夫去找对应的妻子
- 步骤分析
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取到每一个键。用增强for实现
- 根据键去找值。用get(Object key)方法实现
public class MapDemo01 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
Set<String> keySet = map.keySet();
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
方式二
- 遍历思路
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 获取所有结婚证的集合
- 遍历结婚证的集合,得到每一个结婚证
- 根据结婚证获取丈夫和妻子
- 步骤分析
- 获取所有键值对对象的集合
- Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
- 遍历键值对对象的集合,得到每一个键值对对象
- 根据键值对对象获取键和值
- 用getKey()得到键
- 用getValue()得到值
public class MapDemo02 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> me : entrySet) {
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
HashMap
??HashMap的实现原理:HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。(其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合) ??在重写equals的方法的时候,必须注意重写hashCode方法,同时还要保证通过equals判断相等的两个对象,调用hashCode方法要返回同样的整数值。而如果equals判断不相等的两个对象,其hashCode可以相同(只不过会发生哈希冲突,应尽量避免)。 ??附 HashMap put方法逻辑图(JDK1.8)
TreeMap
??在Map集合框架中,除了HashMap以外,TreeMap也是常用到的集合对象之一。 ??与HashMap相比,TreeMap是一个能比较元素大小的Map集合,会对传入的key进行了大小排序。其中,可以使用元素的自然顺序,也可以使用集合中自定义的比较器来进行排序; ??不同于HashMap的哈希映射,TreeMap实现了红黑树的结构,形成了一颗二叉树。 TreeMap排序 (1)使用元素自然排序 ??在使用自然顺序排序时候,需要区分两种情况:一种是Jdk定义的对象,一种是自己定义的对象; (2)使用自定义比较器排序 ??使用自定义比较器排序,需要在创建TreeMap对象时,将自定义比较器对象传入到TreeMap构造方法中; ??自定义比较器对象,需要实现Comparator接口,并实现比较方法compare(To1,To2); ??使用自定义比较器排序的话,被比较的对象无需再实现Comparable接口了;
Java面试中常见集合面试题
集合框架底层数据结构总结
- List
Arraylist:Object数组 Vector: Object数组 LinkedList: 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环) - Set
HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素 LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。 TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。) - Map
HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。 Hashtable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 TreeMap: 红黑树(自平衡的排序二叉树)
List、Set、Map三者特点
1、List(有序、可重复) ??List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。 2、Set(无序、不能重复) ??Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。 3、Map(键值对、键唯一、值不唯一) ??Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
ArrayList和Vector有何异同点?
ArrayList和Vector在很多时候都很类似 (1)两者都是基于索引的,内部由一个数组支持。 (2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。 (3)ArrayList和Vector的迭代器实现都是fail-fast的。 (4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。
以下是ArrayList和Vector的不同点 (1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。 (2)ArrayList比Vector快,它因为有同步,不会过载。 (3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。
ArrayList和LinkedList有何区别?
ArrayList和LinkedList两者都实现了List接口,但是它们之间有些不同。 (1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList存储一系列的节点数据,每个节点都与前一个和下一个节点相连接。所以,尽管有使用索引获取元素的方法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),比ArrayList要慢。 (2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快,因为在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。 (3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用。
HashMap和Hashtable有什么区别?
???HashMap 和 Hashtable 都实现了 Map 接口,因此很多特性非常相似。但是,他们有以下不同点: ???HashMap 允许键和值是 null,而 Hashtable 不允许键或者值是 null。 ???Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线程环境,而 Hashtable适合于多线程环境。 ???HashMap 提供了可供应用迭代的键的集合,因此,HashMap 是快速失败的。另一方面,Hashtable 提供了对键的列举(Enumeration)。 一般认为 Hashtable 是一个遗留的类。
总结
???Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。 ???集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。 ???集合框架的类和接口均在java.util包中。 ???任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。
斗地主源码
链接:https://pan.baidu.com/s/1JuOyirPztuj9b78_ASDUQg 提取码:yy52 斗地主源码
|