简介
集合又称容器。是Java中对数据结构(数据存储方式)的具体实现。
我们可以利用集合存放数据,也可以对集合进行新增、删除、修改、查看等操作。
集合中数据都是在内存中,当程序关闭或重启后集合中数据会丢失。所以集合是一种临时存储数据的容器。
JDK中集合结构图
集合作为一个容器,可以存储多个元素,但是由于数据结构的不同,java提供了多种集合类。将集合类中共性的功能,不断向上抽取,最终形成了集合体系结构。
List接口和Set接口
List和Set的父接口
1. List接口:存储有序, 可重复数据。
1. Vector:List的实现类,底层为可变长度数组实现。所有方法都是同步操作(线程安全),每次扩容成本增长。新数组长度为原数组长度的2倍
2. ArrayList:List的实现类,底层为可变长度数组实现。所有方法都是非同步操作(非线程安全的),以1.5倍的方式在扩容。常用于: 查询较多的情况
3. LinkedList:List的实现类,双向非循环链表的实现。常用于: 删 增 较多的情况
Set接口:存储无序,不可重复数据。
1. HashSet:Set实现类,底层是HashMap 散列表(数组+链表+(红黑树 jdk1.8及之后))。所有添加到 HashSet 中的元素实际存储到了HashMap的key中
2. LinkedHashSet:HashSet子类. 使用LinkedHashMap来存储它的元素,存储的值插入到LinkedHashMap的可以key中, 底层实现(数组+链表+(红黑树 jdk1.8及之后) + 链表), 可以记录插入的顺序
3. TreeSet:Set实现类,底层是TreeMap(红黑树实现), 存入到TreeSet中的元素, 实际存储到了TreeMap中, 根据存储元素的大小可以进行排序
2.2 Map接口
Map:独立的接口。每个元素都包含Key(名称)和Value(要存储的值)两个值。
1. HashMap:Map实现类, 对散列表 (数组+链表+(红黑树Java8及之后))的具体实现,非同步操作(非线程安全的)。存储时以Entry类型存储(key, value)
2. LinkedHashMap: HashMap的子类,是基于HashMap和链表来实现的。在hashMap存储结构之上再添加链表, 链表只是为了保证顺序
3. TreeMap:Map实现类, 使用的不是散列表, 而是对红黑树的具体实现。根据key值的大小, 放入红黑树中, 可实现排序的功能(key值大小的排序)
4. HashTable:Map实现类, 和HashMap数据结构一样,采用散列表(数组+链表+(红黑树 jdk1.8及之后))的方法实现, 对外提供的public函数几乎都是同步的(线程安全)。
常用的集合: ArrayList, HashMap, HashSet
ArrayList
ArrayList 实现了List接口, 底层实现可变长度数组。存储有序、可重复数据, 有下标
实例化
常用向上转型进行实例化。绝大多数集合都支持泛型,如果不写泛型认为泛型是<Object>,使用集合时建议一定要指定泛型。
List<泛型类型> 对象 = new ArrayList<>();
内存结构图
常用API
public class TestArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//按照下标添加
list.add("aa");
//添加到指定的位置
list.add(1, "bb");
//查看元素个数
int size = list.size();
//根据指定的下标修改
list.set(0, "aaa");
//判断
int bb = list.indexOf("bb"); //元素存在, 返回元素的下标. 不存在, 返回-1
boolean b = list.contains("bb"); //元素存在, 返回true. 不存在, 返回false
//删除
list.remove(0); //根据下标删除
list.remove("bb"); //根据指定元素删除
//更新
list.set(1, 65);
//查询
for (String s : list) {
System.out.println(s);
}
for (int i = 0; i < size; i++) {
System.out.println(list.get(i));
}
}
}
LinkedList
介绍
LinkedList是Java中对双向非循环链表的实现。实现了List接口。
具有ArrayList所有常用方法,额外还添加了头尾操作方法(实现了Deque接口),这些方法在List接口中是不存在的,所以如果希望使用这些头尾操作方法,实例化时不用向上转型。
实例化语法
LinkedList<泛型> 对象= new LinkedList<>();
常用API
ArrayList里面常用API在LinkedList中都可以使用。
下面的演示为LinkedList比ArrayList多的常用方法。
public class TestLinkedList {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.addFirst("aa");
linkedList.addLast("bb");
//获取头结点 没有头结点 java.util.NoSuchElementException
String first = linkedList.getFirst();
System.out.println(first);
//获取尾结点 没有尾结点 java.util.NoSuchElementException
String last = linkedList.getLast();
System.out.println(last);
//获取头结点 没有头结点 返回 null
String s = linkedList.peekFirst();
System.out.println(s);
//获取尾结点 没有尾结点 放回 null
String s1 = linkedList.peekLast();
System.out.println(s1);
//删除头结点 没有头结点 java.util.NoSuchElementException
String s2 = linkedList.removeFirst();
System.out.println(s2); //被删除结点中的值
//删除尾结点 没有尾结点 java.util.NoSuchElementException
String s3 = linkedList.removeLast();
System.out.println(s3);//被删除结点中的值
//删除头结点 没有头结点 返回 null
String s4 = linkedList.pollFirst();
System.out.println(s4);
//删除尾结点 没有尾结点 放回 null
String s5 = linkedList.pollLast();
System.out.println(s5);
}
}
问题1:将ArrayList替换成LinkedList之后,变化的是什么?
底层的结构变了
ArrayList:数组 LinkedList:双向非循环链表
问题2:到底是使用ArrayList还是LinkedList
根据使用场合而定
大量的根据索引查询的操作,大量的遍历操作(按照索引0--n-1逐个查询一般),建议使用ArrayList
如果存在较多的添加、删除操作,建议使用LinkedList
问题3:LinkedList增加了哪些方法
增加了对添加、删除、获取首尾元素的方法
addFirst()、addLast()、removeFirst()、removeLast()、getFirst()、getLast()
Java中栈和队列的实现类
Vector过时了,被ArrayList替代了,Stack也就过时了
public class Stack<E> extends Vector<E>
Deque和Queue的实现类,用的非常少了解即可 1.ArrayDeque 顺序栈 数组 2.LinkedList 链栈 链表
public interface Queue<E> extends Collection<E> public interface Deque<E> extends Queue<E>
早期的栈结构实现类 Stack
public class Test1 {
public static void main(String[] args) {
Stack<String> stack =new Stack<String>();
// 入栈方法
stack.push("马云");
stack.push("马化腾");
stack.push("马明哲");
stack.push("马老师");
// 跳栈 弹栈 取出栈顶元素
String pop = stack.pop();
System.out.println(pop);
System.out.println(stack);
}
}
Queue单端队列
public class Test2 {
public static void main(String[] args) {
Queue<String> q=new LinkedList<String>();
// 入队方法
q.offer("张三丰");
q.offer("张翠山");
q.offer("张无忌");
System.out.println(q);
// 出队方法 取出队首
String poll = q.poll();
System.out.println(poll);
System.out.println(q);
}
}
Deque双端队列
public class TestLinkedList2 {
public static void main(String[] args) {
//摞盘子
Deque<String> deque1 = new LinkedList<String>();
deque1.offerFirst("盘子1");//队首存值
deque1.offerLast("盘子2");//队尾存值
deque1.pollFirst();//队首取值
deque1.pollLast();//队尾取值
System.out.println(deque1.size());
}
}
HashSet
介绍
完全基于HashMap(数组+链表+(红黑树))实现的。
存储无序, 无下标, 元素不重复数据。
代码示例
public class HashSetTest {
public static void main(String[] args) {
Set<Student> set = new HashSet<>();
set.add(new Student("张三", 12,1));
set.add(new Student("李四", 12,2));
set.add(new Student("王五", 12,3));
set.add(new Student("张三", 12,4));
System.out.println(set);
}
}
//[Student{name='王五', age=12, id=3}, Student{name='张三', age=12, id=1}, Student{name='李四', age=12, id=2}]
注意
HashSet 保证唯一方式 必须重写hashcode和equals
TreeSet
介绍
底层是基于TreeMap红黑树。红黑树底层进行会进行值比较 使用 Comparable中CompareTo进行 所以存储泛型类必须实现 Comparable接口或外部定义类实现Comparator接口
代码示例
//实现Comparable接口并 重写compareTo方法 否则报Student cannot be cast to java.lang.Comparable错误
public class Student implements Comparable<Student> {
private String name;
private int age;
private int id;
public Student(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
id == student.id &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, id);
}
//自定义比较内容
@Override
public int compareTo(Student o) {
return this.id - o.id;
}
}
//或自定义类实现Comparator接口
public class Compares implements Comparator<Teacher> {
@Override
public int compare(Teacher o1, Teacher o2) {
return o1.getAge() - o2.getAge();
}
}
public class TreeSetTest {
public static void main(String[] args) {
Set<Student> set = new TreeSet<>();
set.add(new Student("张三", 12,1));
set.add(new Student("李四", 12,2));
set.add(new Student("王五", 12,3));
set.add(new Student("张三", 12,1));
System.out.println(set);
//也可 通过外部类实现Comparator TreeSet构造设置
// Set<Teacher> set = new TreeSet<>(new Compares());
// set.add(new Teacher( 12,1));
// set.add(new Teacher( 13,1));
// set.add(new Teacher( 12,1));
// System.out.println(set);
}
}
HashMap与TreeMap
介绍
HashMap与HashTree实现 Map接口与Collection没有关系 Map中每个元素都是Entry类型,每个元素都包含Key(键)和Value(值)二者十分类似
HashMap是对散列表的具体实现。
TreeMap是红黑树的具体实现。
里面都包含Key-Value值。
代码示例
public class HashMapTest {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
Map<Integer,String> map2 = new TreeMap<>();
map.put(1, "bj");
map.put(2, "bj2");
map.put(3, "bj3");
map.put(1, "bj4");
map.put(null, "bj5");
map.put(null, "bj6"); //hashmap允许null
System.out.println(map);
// map2.put(1, "bj");
// map2.put(2, "bj2");
// map2.put(3, "bj2");
// map2.put(1, "bj4");
map2.put(null, "bj5"); //treemap 不允许null
// System.out.println(map2);
System.out.println("===========map遍历1============");
map.forEach((k,v)->{
System.out.println(k+"---"+v);
});
System.out.println("===========map遍历2============");
Set<Integer> integers = map.keySet();
for (Integer key : integers) {
System.out.println(key+"---"+map.get(key));
}
System.out.println("===========map遍历3============");
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
System.out.println("===========map遍历4============");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey()+"---"+entry.getValue());
}
}
}
Iterator
简介
中文名称:迭代器。是一个接口,每个集合中实现类都对Iterator提供了内部类的实现。
通过Iterator可以实现遍历集合的效果。
仅collection实现类可用 map不可用 增强for循环底层为迭代器
存在意义:
隐藏集合实现细节,无论是哪种集合都是通过Iterator进行操作,而不是直接操作集合。通过一套API实现所有集合的遍历。
可以在遍历时删除集合中的值。
代码示例
public class IteratorTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
// list.remove(1); //ConcurrentModificationException 删除或插入报错
iterator.remove(); //删除当前内容
}
for (Integer integer : list) {
// list.remove(0); //ConcurrentModificationException 不可用
}
for (int i = 0; i < list.size(); i++) {
list.remove(0); //无影响
}
}
}
Collections
介绍
Collections是一个工具类型,一个专门操作集合的工具类。
代码示例
public class TestCollections {
public static void main(String[] args) {
//添加元素
List<Integer> list = new ArrayList();
Collections.addAll(list, 10, 50, 30, 90, 85, 100);//6
System.out.println(list);
//排序
Collections.sort(list);//默认按照内部比较器
System.out.println(list);
//查找元素(元素必须有序)
int index = Collections.binarySearch(list, 500);//不存在返回负数
System.out.println(index);
//获取最大值和最小值
int max = Collections.max(list);
int min = Collections.min(list);
System.out.println(max + " " + min);
//填充集合
Collections.fill(list, null);
System.out.println(list);
//复制集合
List list2 = new ArrayList();
Collections.addAll(list2, 10, 20, 30, 50);
System.out.println(list2);
Collections.copy(list, list2);//dest.size >= src.size 目标列表的长度至少必须等于源列表。
System.out.println(list);
//同步集合
//StringBuffer 线程安全效率低 StringBuilder 线程不安全,效率高
//Vector 线程安全 效率低 ArrayList 线程不安全,效率高
//难道是要性能不要安全吗,肯定不是。
//在没有线程安全要求的情况下可以使用ArrayList
//如果遇到了线程安全的情况怎么办
//方法1:程序员手动的将不安全的变成安全的
//方法2:提供最新的线程安全并且性能高的集合类
List list3 = new ArrayList();
Collections.addAll(list3, 10, 90, 30, 40, 50, 23);
System.out.println(list3);
//将list3转换成线程安全的集合类
list3 = Collections.synchronizedList(list3);
//下面再操作,就线程安全了
}
}