数据结构:把多个数据按照一定的存储方式,存储起来,称存储方法为数据结构。
数组是最简单的数据结构。
数据的存储方式有:数组,队列,链表,栈,哈希表等。
不同的数据结构性能不同,所以要选比较合适的,不能乱用。
数据结构的作用:
1:最基本的作用:可以对应用中多个数据做存储(模拟生活数据的存储)。
2:面向对象设计:作为程序员开发的中间工具。
一、 Vector : 是一个动态数组,其底层依然是一个Object[ ] 数组,和我们编写的MyArrayList是一样的,既然是对数组做封装,所以包含了对数组的增删改查操作。
Vector和ArrayList的关系:
1):底层算法都基于数组。
2):ArrayList是集合框架里提供的新的变长数组,Vector是ArrayList的前身。
3):Vector相对于ArrayList来说,线程更安全,但性能较低。
public class VectorDemo {
public static void main(String[] args) {
Vector vector=new Vector(10);
//boolean add(E e)将指定元素添加到此Vector的末尾。
//void add(int index, E element)在此Vector的指定位置插入指定的元素。
//void addElement(E obj)将指定的组件添加到此Vector的末尾,将其大小增加 1。
vector.add("A");
vector.addElement("B");
// add方法和 addElement方法差不多(通过看底层源码)
System.out.println(vector);// 直接调用toString方法
vector.add(0,"C");
System.out.println(vector);
//boolean addAll(Collection extends E> c)
Vector v1=new Vector();
v1.add(1);
v1.add(2);
v1.add(3);
vector.add(v1); // 把 v1 当做整体存放在 vector 中
vector.addAll(v1);// 把 v1的每一个元素存放在 vector中
System.out.println(vector);
//boolean contains(Object o) 判断当前集合是否包含元素 o
System.out.println(vector.contains(1));
//boolean containsAll(Collection> c) 判断当前集合是否包含集合 o
System.out.println(vector.contains(v1));
// 集合里面存放的是对象的引用,而不是对象的数据(通过地址来实现操作)
Vector v2=new Vector();
StringBuilder str=new StringBuilder("Will");
v2.add(str);
System.out.println(v2);
str.append("lalala");
System.out.println(v2);
}
}
拓展:在集合中,只能存储对象,不能存储基本数据类型。
也就是说,byte , short , char , int , long , float , double , boolean不能支持。
Java5之前,我们使用集合来存储数据,得自己装箱。
例如:集合对象.add(Integer valueOf(1));
但是java5开始,可以自动装箱;
例如:集合对象.add(1);
二、 栈:底层依然是数组:
特点:先进后出(LIFO)
三个操作:
1、压入到栈顶 :push
2、弹出栈顶元素 :peek
3、移除栈顶元素 :pop
Java.until.Stack是Vector的子类。
三、 基于数组的集合类,如ArrayList的构造器:
Public ArrayList() {}
Public ArrayList(int initialCapacity) {}
扩容问题:
Public ArrayList() {}
在以前,默认创建的是一个容量为10的数组,底层:this(10);
现在默认创建的是一个没有元素的空数组。底层:new Object[] {}。虽然创建了没有元素的空数组,但是当第一次使用add方法的时候,此时才设定初始容量为10。
四、 LinkedList类:底层使用的单链表操作/双向链表/单向队列/双向队列。并且与头插,尾插以及频繁改变元素相关的操作十分方便。
五、 集合继承体系( 以后会贴一张详细继承图 ):
Iterable接口:支持集合的迭代操作。
Collection接口:表示集合/容器。
Collection接口有两个非常重要的子接口:List/Set。
List接口:表示一种,可以允许元素重复,并且会记录添加顺序的集合。
最常用的实现类:ArrayList,LinkedList。
Set接口:表示一种,不允许元素重复,并且也不会记录添加顺序的集合。
最常用的实现类:HashSet,TreeSet。对于Set接口的实现类中,去重操作只对基本类型有效,对于自定义类的对象的去重必须重写equals和hasCode方法。
六、 迭代器和增强for循环:
public class IteratorDemo {
public static void main(String[] args) {
List list=new ArrayList();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
System.out.println(list);
// 取出集合中的各元素 (传统做法)
for(int i=0;i<list.size();i++){ // 第一种方法
Object ele=list.get(i);
System.out.println(ele);
}
System.out.println("-----------------------------------");
// 更佳做法:使用迭代器
Iterator it=list.iterator(); //第二种方法
while(it.hasNext()){ //判断是否有下一个元素
System.out.println(it.next());// 让指针指向下一个元素
}
System.out.println("---------------------------------------");
/* 另外一种操作
for(Iterator it=list.iterator();it.hasNext();){
System.out.println(it.next());-------------->这里会自增
}
此时 it 对象在for循环结束就销毁,相比于在方法里创建迭代器对象,空间使用更合理。
*/
Vector v=new Vector(); //第三种方法
v.add(1);
v.add(2);
v.add(3);
Enumeration elements=v.elements();// 此接口的功能与 Iterator 接口的功能是重复的,新的实现应该优先考虑使用 Iterator 接口而不是 Enumeration 接口。
while(elements.hasMoreElements()){
System.out.println(elements.nextElement());
}
System.out.println("---------------------------------------");
// 最简单的迭代方式
for(Object ele : list){
System.out.println(ele);
}
/*
for(Object ele : 数组 / Iterable接口的实现类的的对象){
System.out.println(ele);
}
*/
}
}
增强for 循环(for-each循环):
for(Object ele : 数组 / Iterable接口的实现类的的对象){
System.out.println(ele);
}
操作数组的时候:底层其实就是普通的for循环。
操作集合的时候:底层使用的是Iterator迭代器。
七、 Set接口:
public class SetDemo {
public static void main(String[] args) {
Set set=new HashSet();
set.add("A"); // 重复的元素被覆盖掉了
set.add("A");
set.add("A");
set.add("B");
System.out.println(set);
// Set-->List
List list=new ArrayList(set);
list.add("C");
list.add("A");
System.out.println(list);
//List-->Set
Set set1=new HashSet(list);
System.out.println(set1);
}
}
HashSet类:
class Student extends Object{
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
// ctrl+ins生成 是否为同一对象的评判可以调控:选择name 或者 age 作为评判,默认是两个一起的
//这里改写为 name
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
/*
在哈希表中,每一个对象的存储位置是不同的,通过对象的hashCode()和equals()决定( hashCode值相同,equals为true时,才被认为是同一个对象)。
*/
public class HashSetDemo2 {
public static void main(String[] args) {
Student stu1=new Student("赵大",1);
Student stu2=new Student("赵大",2);
Student stu3=new Student("孙三",3);
Student stu4=new Student("李四",4);
Set set=new HashSet();
set.add(stu1);
set.add(stu2);
set.add(stu3);
System.out.println(set);
}
}
LinkedHashSet类是HashSet类的子类:同时体现了双向链表和哈希表算法。
哈希表算法:决定了存储位置。
双向链表算法:用来保证在迭代时候的顺序就是插入时的顺序。
LinkedHashSet类相对于HashSet来说,性能更低,因为需要保证输出顺序。
// LinkedHashSet既要保证不能重复,又要保证输入有序。
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set set=new HashSet();
set.add("5");
set.add("6");
set.add("3");
set.add("4");
System.out.println(set);
set=new LinkedHashSet();
set.add("5");
set.add("6");
set.add("3");
set.add("4");
System.out.println(set);
}
}
问题:为什么不直接使用List的实现类?
因为LinkedHashSet类不仅可以保证添加顺序还保证元素不能重复。