1 泛型
1.1 泛型的概念
泛型的本质就是“数据类型的参数化”。我们可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。 例如,下面的List的泛型为Person ,表示List内存储的元素为Person的对象:
public class TestGerner {
public static void main(String[] args) {
List<Person> persons = new ArrayList<>();
persons.add(new Person("AA",12));
persons.add(new Person("BB",12));
persons.add(new Person("CC",12));
Person person = persons.get(0);
}
}
2 Collection接口
2.1 集合架构
? Collection 接口存储一组不唯一,无序的对象 ? List 接口存储一组不唯一,有序(索引顺序)的对象 ? Set 接口存储一组唯一,无序的对象 ? Map接口存储一组键值对象,提供key到value的映射 ? Key 唯一 无序 ? value 不唯一 无序
2.2 List
List是有序、可重读的容器。 有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。 可重复: List允许加入重复的元素。更确切地讲,List通常允许满足e1.equals(e2)的元素重复加入容器。 List接口常用的实现类有3个:ArrayList、LinkedList和Vector。 ArrayList底层使用数组实现的存储。特点:查询效率高,增删效率低,线程不安全数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制。
2.3 Map接口
? 特点 key-value映射 ? HashMap ? Key无序 唯一 (Set) ? Value 无序 不唯一 (Collection)
? LinkedHashMap ? 有序的HashMap 速度快
? TreeMap ? 有序 速度没有hash快 ? 问题:Set和Map有关系吗? ? 采用了相同的数据结构,只用于map的key存储数据,就Set。 Map中常用的方法:
2.3.1 HashMap存储键值对底层过程
HashMap底层实现采用了哈希表,这是一种非常重要的数据结构。 哈希表的基本结构就是“数组+链表”。 数据结构中由数组和链表来实现对数据的存储,他们各有特点。 (1)数组︰占用空间连续。寻址容易,查询速度快。但是,增加和删除效率非常低。 (2)链表︰占用空间不连续。寻址困难,查询速度慢。但是,增加和删除效率非常高。 那么,我们能不能结合数组和链表的优点(即查询快,增删效率也高)呢?答案就是“哈希表”。 哈希表的本质就是“数组+链表”。 其中Entry[] table就是HashMap的核心数据结构,我们也称之为“位桶数组”。 一个Entry对象存储了: 1.key:键对象value:键值对 2.next:下一个节点 3.hash:键对象的hash值
2.3.2 HashMap查找键值对底层过程
取数据过程get(key) 我们需要通过key对象获得“键值对”对象,进而返回value对象。明白了存储数据过程,取数据就比较简单了,参见以下步骤︰ (1)获得key的hashcode,通过hash()散列算法得到hash值,进而定位到数组的位置。 (2)在链表上挨个比较key对象。调用equals()方法,将key对象和链表上所有节点的key对象进行 比较,直到碰到返回true的节点对象为止。 (3)返回equals()为true的节点对象的value对象。 明白了存取数据的过程,我们再来看一下hashcode()和equals方法的关系︰ Java中规定,两个内容相同(equals()为true)的对象必须具有相等的hashCode。因为如果equals()为true而两个对象的hashcode不同;那在整个存储过程中就发生了悖论。 扩容问题 HashMap的位桶数组,初始大小为16。实际使用时,显然大小是可变的。如果位桶数组中的元素达到(0.75*数组length),就重新调整数组大小变为原来2倍大小。 扩容很耗时。扩容的本质是定义新的更大的数组,并将旧数组内容挨个拷贝到新数组中。
2.3.3 TreeMap的使用和底层实现
TreeMap是红黑二叉树的经典实现。
2.3.4 HashMap与HashTable的区别
(1)HashMap:线程不安全,效率高。允许key或value为null。 (2)HashTable:线程安全,效率低。不允许key或value为null。
3 Set接口
Set接口继承自Collection, Set接口中没有新增方法,方法和Collection保持完全一致。我们在前面通过List学习的方法,在Set中仍然适用。因此,学习Set的使用将没有任何难度。 Set容器特点∶无序、不可重复。无序指Set中的元素没有索引,我们只能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;甚至,Set中也只能放入一个null元素,不能多个。 Set常用的实现类有:HashSet、TreeSet等,一般使用HashSet。 3.1 TreeSet的使用和底层实现 同HashSet的使用大致一样,但是为有序,不可重复。 4. 使用Iterator迭代器遍历容器元素(List/Set/Map) 所有集合类均未提供相应的遍历方法,而是把把遍历交给迭代器完成。迭代器为集合而生,专门实现集合遍历。 Iterator是迭代器设计模式的具体实现。 ? Iterator方法 ? boolean hasNext(): 判断是否存在另一个可访问的元素 ? Object next(): 返回要访问的下一个元素 ? void remove(): 删除上次访问返回的对象。
4 集合的遍历
4.1 Iterator遍历List
public static void main(String[] args) {
testIteraterList();
}
public static void testIteraterList(){
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
for (Iterator<String> iter = list.iterator();iter.hasNext();){
String temp = iter.next();
System.out.println(temp);
}
}
4.2 Iterator遍历Map
(1)第一种方式
public static void main(String[] args) {
testIteraterList();
}
public static void testIteraterList(){
Map<Integer, String> map = new HashMap<>();
map.put(100,"aaa");
map.put(200,"bbb");
map.put(300,"ccc");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Iterator<Map.Entry<Integer, String>> iter = entries.iterator();iter.hasNext();){
Map.Entry<Integer, String> entry= iter.next();
System.out.println(entry);
}
}
(2)第二种方式
Set<Integer> set = map.keySet();
for ( Iterator<Integer> iter = set.iterator();iter.hasNext();){
Integer key = iter.next();
System.out.println(key+"---"+map.get(key));
}
5.集合总结
5.1 集合和数组的比较
? 数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下: ? 1 : 数组容量固定且无法动态改变,集合类容量动态改变。 ? 2:数组能存放基本数据类型和引用数据类型的数据,而集合类中只能放引用数据类型的数据。 ? 3:数组无法判断其中实际存有多少元素,length只告诉了array容量;集合可以判断实际存有多少元素,而对总的容量不关心。 ? 4:集合有多种数据结构(顺序表、链表、哈希表、树等)、多种特征(是否有序,是否唯一)、不同适用场合(查询快,便于删除、有序),不像数组仅采用顺序表方式。 ? 5:集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性调用即可实现各种复杂操作,大大提高软件的开发效率。
5.2 ArrayList和LinkedList 的联系和区别
? 联系: ? 都实现了List接口 ? 有序 不唯一(可重复) ? ArrayList ? 在内存中分配连续的空间,实现了长度可变的数组 ? 优点:遍历元素和随机访问元素的效率比较高 ? 缺点:添加和删除需大量移动元素效率低,按照内容查询效率低 ? LinkedList ? 采用链表存储方式。 ? 缺点:遍历和随机访问元素效率低下 ? 优点:插入、删除元素效率比较高(但是前提也是必须先低效率查询才可。如果插入删除发生在头尾可以减少查询次数)。
5.3 Vector和ArrayList的联系和区别
? Vector和ArrayList的联系和区别 ? 实现原理相同,功能相同,都是长度可变的数组结构,很多情况下可以互用 ? 两者的主要区别如下 ? Vector是早期JDK接口,ArrayList是替代Vector的新接口 ? Vector线程安全,效率低下;ArrayList重速度轻安全,线程非安全 ? 长度需增长时,Vector默认增长一倍,ArrayList增长50%
5.4 HashMap和Hashtable的联系和区别
? 实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用 ? 两者的主要区别如下 ? Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口 ? Hashtable继承Dictionary类,HashMap实现Map接口 ? Hashtable线程安全,HashMap线程非安全 ? Hashtable不允许null值,HashMap允许null值
5.5 Collection和Collections的区别
? Collection是Java提供的集合接口,存储一组不唯一,无序的对象。它有两个子接口 List和Set。 ? Java中还有一个Collections类,专门用来操作集合类 ,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
6 Collections工具类
类java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。 1.void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。 2. void shuffle(List) //对List容器内的元素进行随机排列。 3.void reverse(List) //对List容器内的元素进行逆续排列。4.void fill(List, Object)//用一个特定的对象重写整个List容器。 5. int binarySearch(List,object) //对于顺序的List容器,采用折半查找的方法查找特定对象,查找成功则返回该对象所在的位置。
7 使用容器存储表格数据
7.1 map和list结合存储整张表
使用容易存储表格数据: 存储方方式: 每一行数据使用一个:Map 整个表格使用一个:List ORM思想:对象关系映射。 设计思想:map表示一行数据,多行数据是多个map;将多个map放到list中。
public class TestStoreData {
public static void main(String[] args) {
Map<String, Object> row1 = new HashMap<>();
row1.put("id", 1001);
row1.put("姓名", "张三");
row1.put("薪水", 2000);
row1.put("入职日期", "2018.5.5");
Map<String, Object> row2 = new HashMap<>();
row2.put("id", 1002);
row2.put("姓名", "李四");
row2.put("薪水", 30000);
row2.put("入职日期", "2005.5.5");
Map<String, Object> row3 = new HashMap<>();
row3.put("id", 1003);
row3.put("姓名", "王五");
row3.put("薪水", 3000);
row3.put("入职日期", "2020.5.4");
List<Map<String, Object>> table1 = new ArrayList<>();
table1.add(row1);
table1.add(row2);
table1.add(row3);
for (Map<String, Object> row : table1) {
Set<String> keySet = row.keySet();
for (String key : keySet){
System.out.print(key+":"+row.get(key)+" ");
}
System.out.println();
}
}
}
7.2 javaBean和list结合存储整张表
设计思想: 每一行数使用一个:javabean对象; 整个表格使用一个Map、List。
public class TestStoreData2 {
public static void main(String[] args) {
User user1 = new User(1001,"张三",20000,"2018.5.5");
User user2 = new User(1002,"李四",30000,"2005.4.5");
User user3 = new User(1003,"张三",3000,"2020.5.4");
List<User> table = new ArrayList<>();
table.add(user1);
table.add(user2);
table.add(user3);
for (User user:table){
System.out.print(user.getId()+":"+user.getName()+":"+" "+user.getSalary()+":"+" "+user.getData());
System.out.println();
}
Map<Integer,User> map = new HashMap<>();
map.put(1,user1);
map.put(2,user2);
map.put(3,user3);
Set<Integer> keySet = map.keySet();
for (Integer key:keySet){
System.out.print(map.get(key).getId()+" "
+map.get(key).getName()+" "
+map.get(key).getSalary()+""
+map.get(key).getData());
System.out.println();
}
}
}
class User{
private int id;
private String name;
private double salary;
private String data;
public User() {
}
public User(int id, String name, double salary, String data) {
this.id = id;
this.name = name;
this.salary = salary;
this.data = data;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
|