集合和数组的区别
数组:用来存储相同数据类型的一个容器,数组一旦确定,长度不可变
集合:用来存放对象引用的一个容器
区别
数组:
1)数组的长度是不可变的,集合的长度是可变的
2)数组可以存基本数据类型和引用数据类型
集合: 只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
集合体系结构
单列集合的体系:
Collection 单列集合的顶层接口
List 有序集合子接口
ArrayList 底层数组:查询快、增删慢,线程不安全
LinkedList 底层链表:查询慢、增删快,线程不安全
Vector 底层数组:查询快、增删慢,线程安全效率低
集合体系 Set 无序集合的子接口
HashSet 底层hashmap,线程不安全
LinkedHashSet 底层LinkedHashMap有序,线程不安全
TreeSet 底层红黑树,线程不安全
双列集合的体系
Map 双列集合的顶层接口
HashMap 底层:数组+链表+红黑树,线程不安全
LinkedHashMap 底层:数组+链表+红黑树+双向链表,保持遍历顺序和插入顺序一致的问题,线程不安全
TreeMap 底层:红黑树,线程不安全
HashTable 底层同HashMap,线程安全,在put方法前面加上了synchronizen关键字
集合遍历
1)单列集合的遍历
a.迭代器遍历
public class Demo1 {
public static void main(String[] args) {
Collection<Integer> c = new ArrayList<>();
c.add(1);
c.add(2);
c.add(3);
c.add(4);
c.add(5);
//获取迭代器对象
Iterator<Integer> it = c.iterator();
//while循环遍历取值
while (it.hasNext()){
System.out.println(it.next());
}
}
}
b.增强for循环(本质也是迭代器)
public class Demo2 {
public static void main(String[] args) {
Collection<Integer> c = new ArrayList<>();
c.add(1);
c.add(2);
c.add(3);
c.add(4);
c.add(5);
for (Integer integer : c) {
System.out.println(integer);
}
}
}
c.List集合特有的遍历方式
public class Demo3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
d.Set集合的遍历方式: 迭代器遍历, 增强for遍历
e.转为数组再遍历
public class Demo4 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Object[] array = list.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
2)Map集合的遍历方式
a.键遍历
获取Map集合中的所有键,放到一个Set集合中,遍历该Set集合,获取到每一个键,根据键再来获取对应的值。【根据键获取值】
案例
package com.tohka.demos;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo1 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("大郎",1);
map.put("宝强",2);
map.put("羽凡",3);
map.put("亦凡",4);
map.put("乃亮",5);
Set<String> set = map.keySet();
for (String s : set) {
System.out.println(s+"==="+map.get(s));
}
}
}
b.键值对遍历
获取Map<K,V>集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示:entrySet()。
案例
package com.tohka.demos;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo2 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("大郎",1);
map.put("宝强",2);
map.put("羽凡",3);
map.put("亦凡",4);
map.put("乃亮",5);
Set<Map.Entry<String, Integer>> set = map.entrySet();
for (Map.Entry<String, Integer> entry : set) {
System.out.println(entry.getKey()+"=="+entry.getValue());
}
}
}
并发修异常的产生原因和解决办法
导致异常原因: 在迭代器遍历过程中使用集合的引用进行元素的添加和删除
解决方法:
1. 通过for循环遍历集合,在遍历过程中可以进行添加和删除元素
2. 使用迭代器中的remove()方法
3. 使用ListIterator<E> listIterator()返回列表中的列表迭代器(按适当的顺序)。
-- add(E e) 添加元素
-- remove() 移除元素
5.TreeSet的自然排序和比较器排序
自然排序:从小到大(1-n),从a到z
构造方法
TreeSet() : 根据元素的自然排序进行排序
TreeSet(Comparator c) : 根据指定的比较器进行排序
1)自然排序Comparable接口的使用
案例:Student类中有年龄和姓名两个属性, 将几个Student对象存储在集合中, 要求按照年龄从小到大进行排序, 年龄相同时, 按照姓名的字母顺序排序
思路 :
-
Student类实现默认排序的Comparable接口(因为TreeSet空参数构造, 默认调用存储对象中重写的Comparable接口的compareTo方法作为自然排序的规则)
-
Student类重写compareTo排序方法, 排序方法每次比较两个对象是否重复以及大小关系
a : 得出0结果, 证明两个对象重复, 后面的对象无法存储进集合中
b : 得出”正数”结果, 证明后面的对象比前面对象大, 因此需要存储的位置在后
c : 得出”负数”结果, 证明后面的对象比前面对象小, 因此需要存储的位置在前
就是根据compareTo比较对象的结果, 决定对象是否可以存储进入集合, 以及存储在集合中的位置
实体类
package com.tohka.entity;
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 String toString() {
return "Student{" +
"name=‘" + name + ‘\‘‘ +
", age=" + age +
‘}‘;
}
@Override
public int compareTo(Student o) {
int num1 = this.age - o.age;
int num2 = num1 == 0?this.name.compareTo(o.name):num1;
return num2;
}
}
测试代码
package com.tohka.test;
import com.tohka.entity.Student;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest1 {
public static void main(String[] args) {
Set<Student> set = new TreeSet<>();
set.add(new Student("张三",12));
set.add(new Student("李四",15));
set.add(new Student("王五",10));
set.add(new Student("赵六",18));// z
set.add(new Student("田七",18));// t
set.add(new Student("赵六",18));
for (Student student : set) {
System.out.println(student.getName()+"==="+student.getAge());
}
}
}
2)比较器排序
-
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
-
比较器排序,就是让集合构造方法接收Comparator的实现类对象,
重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
实体类
package com.tohka.entity;
public class 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 String toString() {
return "Student{" +
"name=‘" + name + ‘\‘‘ +
", age=" + age +
‘}‘;
}
}
测试代码
package com.tohka.test;
import com.tohka.entity.Student;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest2 {
public static void main(String[] args) {
Set<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num1 = o1.getAge() - o2.getAge();
int num2 = num1 == 0? o1.getName().compareTo(o2.getName()): num1;
return num2;
}
});
set.add(new Student("张三",12));
set.add(new Student("李四",15));
set.add(new Student("王五",10));
set.add(new Student("赵六",18));// z
set.add(new Student("田七",18));// t
set.add(new Student("赵六",18));
for (Student student : set) {
System.out.println(student.getName()+"==="+student.getAge());
}
}
}
HashSet如何保证元素唯一
1.调用元素的hashCode()方法获取元素的哈希值
2.通过哈希值和数组长度获取元素的存储位置(底层是与运算,可以理解为取余)
3.如果该位置没有元素存在,元素唯一,将该元素存储在该位置
4.如果该位置有元素存在,遍历该位置链表的所有元素,和新存入的元素比较哈希值
5.哈希值都不相同,元素唯一,将该元素存储在该位置
6.哈希值相同,调用equals()方法比较内容是否相同
7.内容不相同,元素唯一,将该元素存储在该位置
8.内容相同,元素不唯一,不存储
HashSe如何实现自定义类型的去重
HashSet集合存储JDK定义好的数据类型 : String, Integer,Double...直接可以保证去重复, 全部重写过HashCode和equals, HashSet集合存储自定义引用数据类型 : Student, Person ,Animal...HashSet没有根据对象中的成员变量进行去重动作
重写hashCode()和equals()方法
HashSet底层是通过HashMap来实现的
HashSet中的add方法, 实际上就是的调用了HashMap中的put方法, 因此HashSet中元素唯一, 与HashMap中key值唯一的原理一致,
9.HashMap中key值唯一分析
HashMap存储的是jdk中提供的类型的键,就可以直接保证键的唯一性
HashMap中存储的键,是自定义类型,无法保证键的唯一性需要重写hashCode和equals方法
10.HashMap和HashSet的关系
HashSet是由HashMap实现出来的,HashSet就是HashMap的键的那一列
将HashMap中的值的那一列隐藏掉,就变成了HashSet
HashSet保证元素唯一原理, 就是使用了HashMap中Key值唯一的原理
Map 集合有哪些实现类,分别具有什么特征
HashMap 线程不安全的键值对集合 允许 null 值,key 和 value 都可以
HashTable 线程安全的键值对集合 不允许 null 值,key 和 value 都不可以(不用)
TreeMap 能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器
解决集合线程不安全的问题
1)单列集合
如何使用的 List 集合来保证线程安全
第一种.使用 Vector,线程安全但是效率低
List list = new Vector();
第二种.使用 Collections 中的方法 synchronizedList 将 ArrayList 转换为线程安全的 List
List list = new ArrayList();
list = Collections.synchronizedList(list);
第三种.使用 java.util.current 包下的 CopyOnWriteArrayList(推荐)
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
第四种.在使用前使用synchronized关键字进行同步
2)双列集合
第一种.使用hashtable,线程安全但是效率低
Map map = new Hashtable();
第二种.使用Collections工具类中讲不安全集合转为安全集合的方法
Map map = new HashMap();
map = Collections.synchronizedMap(map);
第三种.使用 java.util.concurrent(并发包) 下的 ConcurrentHashMap
ConcurrentHashMap map = new ConcurrentHashMap();
第四种.在使用前使用synchronized关键字进行同步
ArrayList 与 LinkedList 区别
ArrayList 使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
LinkedList 使用双向链接方式存储数据,每个元素都记录前后元素的指针, 所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或 后几个元素速度比较快。
开发中什么时候到 ArrayList?,我们在做查询的时候把 查询出来的数据经常存到 arraylist 里.
Iterater 和 ListIterator 之间有什么区别?
1,我们可以使用 Iterator 来遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
2,Iterator 只可以向前遍历,而 LIstIterator 可以双向遍历。
3,ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个 元素、获取前面或后面元素的索引位置。
HashTable 和 ConcurrentHashMap 的区别
相同点:它们都可以用于多线程的环境,
不同点:Hashtable 的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。 而ConcurrentHashMap 引入了分割(segmentation),不论它变得多么大,仅仅需要锁定 map 的某个 部分,而其它的线程不需要等到迭代完成才能访问 map。 简而言之,在迭代的过程中,ConcurrentHashMap 仅仅锁定 map 的某个部分,而 Hashtable 则会锁 定整个 map
arrayList初始化的时候创建多大的数组
arrayList底层维护了一个大小为10的 Object[],每次扩容是乘以1.5再加1
如果hashtable存储null值会怎么样?
编译通过,运行报空指针异常
介绍一下ConcurrentHashMap吧
该集合的特点:
1、底层是哈希表结构
2、此集合是线程安全的,但是某些功能不必锁定。比如get()
为了提高效率,不会将整个集合全部锁定。
3、不会抛出ConcurrentModificationException并发修改异常
此集合支持遍历过程中添加,删除元素
TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?
1.TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。
2.TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。
3.Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象必须实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。