集合
一、线性表
1、定义:线性表是一种数据结构。一个线性表是多个元素的有限序列。
2、分类:对于数据不同的逻辑结构,计算机在物理磁盘上通常有两种物理存储结构:
- (1)顺序表、顺序存储结构(线性表):
基本思想:元素的存储空间是连续的。在内存中是以顺序存储,内存划分的区域是连续的。存储结构如下图:
-(2)链表、链序存储结构(链性表):
基本思想:元素的存储空间是离散的,单独的(物理),它们可以通过在逻辑上指针的联系使得它成为了整体的链表。存储结构如下图:
1.单链表
2.循环链表
3.双链表(双向循环表)
(图有点小问题 :最后一个节点的 指针域 也指向头结点)
三者的区别(从上面三个图我们可以总结出来):
- 它们都有数据域(data§)和指针域(next§),但是从图中可以看出双链表有两个指针域,一个指向它的前节点,一个指向它的后节点。
- 单链表最后一个节点的指针域为空,没有后继节点;循环链表和双链表最后一个节点的指针域指向头节点,下一个结点为头节点,构成循环;
- 单链表和循环链表只可向一个方向遍历;双链表和循环链表,首节点和尾节点被连接在一起,可视为“无头无尾”;双链表可以向两个方向移动,灵活度更大。
底层实现源码:
class MyLinklist {
private static class Node {
//数据域
private Object data;
//指针域
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
}
//头结点
private Node head;
private int size;
}
*顺序表和链表的对比:
1. 顺序表优点: 可以根据下标计算元素的地址,查找和修改速度快
2. 顺序表缺点: 增加和删除时需要移动大量元素,增删效率低
3. 链式存储的优点: 插入和删除元素时不需要移动元素的位置,效率高
4. 链式存储的缺点:查找和修改元素时必须从头查找,速度慢
二、栈和队列
1、栈
1、 基本思想:
后进先出(先进后出)即栈中元素被处理时,按后进先出的顺序进行,栈又叫后进先出表(LIFO)。
2.栈的操作:
入栈(push( )),从栈顶增加一个元素
出栈(pop( )),从栈顶删除一个元素
获得栈顶元素,但是不出栈(get( ))
不允许在栈的其它位置插入或删除元素
3、代码实现:
class Stack extends ArrayList {
public void push( Object obj ) {
super.add( super.size( ), obj );
}
public Object pop( ) {
return super.remove( super.size( ) - 1 );
}
public Object get( ) {
return super.get( super.size( ) - 1 );
}
}
很明显Stack继承了ArrayList,代码耦合性高,并不符合java封装的特性 综上所述,引出了聚合:使用聚合关系代替继承关系后,线性表的所有方法被封装起来了,只能通过公开的方法间接地操作线性表,保证了栈中元素的正确性。
具体代码实现:
class Stack {
private List list = new ArrayList( );
public void push( Object obj ) {
this.list.add( this.list.size( ), obj );
}
public Object pop( ) {
return this.list.remove( this.list.size( ) - 1 );
}
public Object get( ) {
return this.list.get( this.list.size( ) - 1 );
}
}
很明显 Stack类被封装了起来 这样减低了代码的耦合性
2、队列:
基本思想:先进先出即先被接收的元素将先被处理,又叫先进先出表(FIFO)。如下图所示:
三、Collection集合
1、定义:一个集合是一个对象,也叫容器,用来保存多个其它对象,保存的对象也叫元素。
2.框架结构:
3、常用方法
方法 | 作用 |
---|---|
add( ) | 往集合里添加一个元素添加一个元素 |
addAll( ) | 往集合里添加另外一个集合 |
clear() | 删除这个集合 |
contains( ) | 是否包含某一个元素(括号里填get(元素下角标)) |
containsAll() | 如果此 collection 包含指定 collection 中的所有元素,则返回 true。 |
isEmpty( ) | 判断集合里是否有元素 |
remove( ) | 删除集合中的元素 |
removeAll( ) | 删除集合中包含另一个集合里的元素 |
retainAll( ) | 移除此 集合中未包含在指定 集合 中的所有元素。 |
size( ) | 获取集合的长度 |
toArray( ) | 将当前集合返回为数组(得以Object类型数组接收) |
四、list集合
1、list集合特点:
1、List中包含的元素有顺序,通过下标体现
2、List接口除了继承Collection接口中的方法,还增加了一些和下标有关的方法。
方法 | 作用 |
---|---|
set() | 替换集合中元素的值 set(元素位置,替换的值) |
indexOf() | 返回此集合中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
lastIndexOf | 返回此集合中最后一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
get() | 返回集合中指定位置的元素。 |
2、List接口的实现类:
1.Vector:使用数组实现的顺序表,效率低
2.ArrayList:使用数组实现的顺序表,为Vector的替代品,比Vector效率高
3.LinkedList:使用节点组成的双链表
3、list集合遍历方法:
1.下标遍历:根据下标访问List集合中的每一个元素的遍历方式,叫做下标遍历。
List list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
2.for-each遍历:
List list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
for ( Object o : list ) {
System.out.println( o );
}
3、用迭代器遍历:
Iterator it = list.iterator( );
while ( it.hasNext( ) ) {
System.out.println( it.next( ) );
}
五、set集合
1、set集合特点:
- Set接口中的所有方法都是Collection接口中声明的方法,没有自己增加的方法。
- Set集合中没有下标的概念。
2、set集合特点:
1.HashSet:使用数组实现,元素的顺序和放入的顺序无关,底层是由哈希表实现的,哈希表是通过使用称 为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放
2.LinkedHashSet:以元素插入的顺序来维护集合的链接表,元素的顺序和放入的顺序一致
3.TreeSet:可以对元素进行排序的Set集合
实例:
HashSet:
Set set = new HashSet( );
set.add( "张三" );
set.add( "李四" );
set.add( "王五" );
set.add( "张三" );
set.add( "王二" );
set.add( "郭帅哥" );
System.out.println( set );
;
运行结果:
出来的顺序和添加顺序不一致 但是相同元素张三不见了。
LinkedHashset:
Set set = new LinkedHashSet( );
set.add( "张三" );
set.add( "李四" );
set.add( "王五" );
set.add( "张三" );
set.add( "王二" );
set.add( "郭帅哥" );
System.out.println( set )
运行结果:
出来的顺序和添加顺序一致 而相同元素张三不见了。
treeHashset:
Set set = new TreeSet( );
set.add( "张三" );
set.add( "李四" );
set.add( "王五" );
set.add( "张三" );
set.add( "王二" );
set.add( "郭帅哥" );
System.out.println( set );
运行结果:
出来的顺序和添加顺序并一致(默认按字符排序) 而相同元素张三不见了。
2、 HashSet或LinkedHashSet放入自定义类型的对象:
1、举例:
Set set = new HashSet( );
set.add( new Student(1,"张三" ) );
set.add( new Student(2,"张三" ) );
System.out.println( set );
运行结果:
很明显 并没有去重
这时我们需要 让Student类重写hashCode( )方法和equals( )方法,让HashSet能够判断两个对象是否重复。
2、解决方案
所以我们只需要让hashCode( )方法返回值相同这样就可以提高一部分效率
public int hashCode( ) {
return 0;
}
重写equals( )方法:
public boolean equals(Object obj) {
// 如果为同一对象的不同引用,则相同
if (this == obj) {
return true;
}
// 如果传入的对象为空,则返回false
if (obj == null) {
return false;
}
// 如果两者属于不同的类型,不能相等
if (getClass() != obj.getClass()) {
return false;
}
// 将object向下强制转型
Employee other = (Employee) obj;
//判断两个类体内部内容是否一致
return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
}
3、总结:
- 把自定义的对象放入HashSet或LinkedHashSet,为保证元素内容不重复,
- 需要: 覆盖hashCode( )方法,保证相同对象返回相同的值,提供调用equals( )方法的机会。
- 覆盖equals( )方法,相同对象返回true。
3、 TreeSet排序方式:
TreeSet是能够给元素排序的Set集合,要给元素排序,必须提供排序规则。
有以下两种方法:
1、元素所属的类实现Comparable接口
class Student implements Comparable {
……
public int compareTo( Object obj ) {
return ……//所提供排序规则
}
}
如果一个对象所属的类实现了Comparable接口,则可以直接把这些对象放入TreeSet集合中,TreeSet集合会自动调用对象的compareTo( )方法,根据返回的结果进行排序:
- 负数:this对象排在obj对象之前
- 正数:this对象排在obj对象之后
- 0:视为重复对象
terrset添加对象实例:
创建学生类代码:
/**
*创建学生类对象 并实现Comparable接口
*
*/
public class WStudent implements Comparable<WStudent>{
/**
* 创建学生类属性
*/
private int id;
private String name;
private String address;
/**
*
* 添加有参构造器 便于创建对象时传值
*
*/
public WStudent(int id,String name,String address){
this.id = id;
this.name = name;
this.address = address;
}
/**
*实现 Comparable接口 提供排序方法
*/
@Override
public int compareTo(WStudent Stu) {
//接收id比较的值
int a = this.id-Stu.id;
//接收name比较的值,由于object类提供compareTo方法 所以可以直接拿来用
int b = this.name.compareTo(Stu.name);
//接收name比较的值,由于object类提供compareTo方法 所以可以直接拿来用
int c = this.address.compareTo(Stu.address);
//提供值相等排序方法
if(a==0){
if(b==0){
return c;//如果id相等,姓名相等,按地址排序
}else{
return b;//如果id相等,按姓名排序
}
}else{
return a;//按id排序
}
}
//重写学生类toString方法
@Override
public String toString() {
return "WStudent [id=" + id + ", name=" + name + ", address=" + address
+ "]";
}
}
测试treeset排序方法
import java.util.Set;
import java.util.TreeSet;
public class TreeSetCalss {
public static void main(String[] args) {
Set<WStudent> set = new TreeSet<WStudent>();
set.add(new WStudent(1,"郭","北京"));
set.add(new WStudent(2,"郭","天津"));
set.add(new WStudent(3,"啊","天津"));
set.add(new WStudent(2,"啊","天津"));
set.add(new WStudent(1,"郭","天津"));
System.out.println(set);
}
}
运行结果如下:
2、使用实现了Comparator接口的对象创建TreeSet对象
如果希望可以对某一类对象按照多个规则进行不同的排序,可以使用Comparator实现类的对象创建TreeSet集合:
Set set = new TreeSet ( new Comparator( ) {
public int compare( Object o1, Object o2 ) {
return ( ( Student ) o1 ).getAge( )
- ( ( Student ) o2 ).getAge( );
}
} );
terrset添加对象实例:
创建学生类:
public class WStudent
{
/**
* 创建学生类属性
*/
private int id;
private String name;
private String address;
/**
*
* 添加有参构造器 便于创建对象时传值
*
*/
public WStudent(int id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public WStudent( ) {
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//重写学生类toString方法
@Override
public String toString() {
return "WStudent [id=" + id + ", name=" + name + ", address=" + address
+ "]";
}
}
创建测试类,并提供排序方法
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetCalss{
public static void main(String[] args) {
Set<WStudent> set = new TreeSet<WStudent>(new MyTreeSet());//匿名内部类
set.add(new WStudent(1,"郭","北京"));
set.add(new WStudent(2,"郭","天津"));
set.add(new WStudent(3,"啊","天津"));
set.add(new WStudent(2,"啊","天津"));
set.add(new WStudent(1,"郭","天津"));
System.out.println(set);
}
}
/**
*提供TreeSet排序方法
*
*实现Comparator接口
*
*/
class MyTreeSet implements Comparator<WStudent> {
@Override
//重写compare方法
public int compare(WStudent o1, WStudent o2) {
//接收id比较的值
int a = o1.getId()-o2.getId();
//接收name比较的值,由于object类提供compareTo方法 所以可以直接拿来用
int b = o1.getName().compareTo(o2.getName());
//接收name比较的值,由于object类提供compareTo方法 所以可以直接拿来用
int c = o1.getAddress().compareTo(o2.getAddress());
if(a==0){
if(b==0){
return c;//如果id相等,姓名相等,按地址排序
}else{
return b;//如果id相等,按姓名排序
}
}else{
return a;//按id排序
}
}
}
使用Comparator实现类的对象创建的TreeSet集合不会使用对象的compareTo( )方法排序,所以不要求对象所属的类必须实现Comparable接口。TreeSet集合根据compare( )方法返回的结果进行排序:
- 负数:o1对象排在o2对象之前
- 正数:o1对象排在o2对象之后
- 0:视为重复对象
两种排序方式比较:
- 使用Comparable只能对对象按照一个规则排序,使用Comparator可以对对象按照多个规则排序,更加灵活。
- 可以把多个Comparator实现类的对象作为待排序对象的静态内部类,使用时从对象中获取。
- 两种排序方法不冲突,可以同时使用。
4、set集合遍历方法:
由于Set集合中没有下标的概念,无法通过下标获得元素,无法使用下标遍历。
所以有以下两种方法:
1、用迭代器遍历
Iterator it = set.iterator( );
while ( it.hasNext( ) ) {
System.out.println( it.next( ) );
}
2、用for-each遍历:
Set set = new HashSet();
…… // 向set中增加元素
for ( Object o : set ) {
System.out.println( o );
}
六、map集合
1、集合特点:
- Map接口是一个独立的接口,和Collection接口没有任何关系。
- 每个元素不是一个对象,而是两个对象。
键对象(key):不能重复
值对象(value):可以重复
Map集合中的一个键值对是一个整体,不能分开。
2.map集合常用方法:
方法 | 作用 |
---|---|
put( ) | 添加元素 |
putAll() | 将另一个集合中的元素全部添加到集合当中 |
get( ) | 获取元素 |
remove( ) | 删除元素 |
clear( ) | 清空集合当中的元素 |
size( ) | 集合的长度 |
isEmpty() | 如果此映射未包含键-值映射关系,则返回 true。 |
containsValue | 如果此映射将一个或多个键映射到指定值,则返回 true。 |
containsKey() | 如果此映射包含指定键的映射关系,则返回 true。 |
3.map的实现类:
- HashMap:无序的键值对的集合,为Hashtable的替代品,比HashTable效率高,线程不安全,可以存空值
- LinkedHashMap:使用链表实现的键值对的集合,键值对的顺序和放入的顺序一致
- TreeMap:可以对键值对按照键对象排序的Map集合,键对象需要实现Comparable接口
- HashTable :线程安全,效率低,不允许存储空值。
- Properties:键对象和值对象都是String类型的哈希表,常用来读写配置文件
4.map的三种遍历
由于map本身是不支持迭代器的,但是map提供了将其元素导入Collection的方法,我们可以将map集合导入Collection的集合当中在进行遍历。
具体操作如下:
1.键遍历
Set<Integer> set = map.keySet();//将map集合中的键值用set集合进行接收
/**
* 迭代器遍历
*
*/
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
2.值遍历
Collection<String> coll = map.values();//将map集合转入Collection的视图表中
/**
* 迭代器遍历
*
*/
Iterator<String> it = coll.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
3.键值遍历
Set<Entry<Integer, String>> set = map.entrySet();//将map集合中的键值对转入Set集合中
/**
* 迭代器遍历
*
*/
Iterator<Entry<Integer, String>> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}