Java学习记录 集合篇

集合类

java.util提供的,集合类可称为容器,集合的长度是可变的(动态长度),集合存放的是对象的引用

Collection接口

Collection接口不能直接使用,但提供了添加元素、删除元素、管理元素等操作 给 List 和 Set 子接口进行操作

常用方法

修饰符 方法 说明
void add(E e) 添加指定对象
void clear() 清空集合中的元素
boolean contains(Object o) 查找一个元素是否存在
void remove(Object o) 指定对象在集合中移除
boolean isEmpty() 判断集合是否为空
int size() 获取集合中的个数
Object[] toArray() 以数组形式返回集合全部内容
Iterator<E> iterator() 集合迭代器(Iterator 接口实例化)

List集合

List集合像列表清单,它允许元素重复,但各个元素的顺序就是对象插入的顺序

List接口及实现类

List接口 继承了 Collection接口,因此也有父接口的方法

方法

返回类型 方法 说明
boolean add(Object obj) 插入数据
boolean add(int index , Object obj) 指定位置插入数据
E get(int index) 获取指定索引的元素
int indexOf(Object o) 根据对象查找指定的位置,不存在则-1
int lastIndexOf(Object o) 从后面向前查找位置,不存在则-1
ListIterator<E> listIterator(int index) 返回从指定位置的 ListIterator 接口的实例
E remove(int index) 删除指定位置的内容
E set(int index , Object obj) 将集合中指定索引位置的对象修改为指定的对象
List<E> subList(int fromIndex , int toIndex) 返回子集合
ListIterator<E> listIterator() 返回特殊的迭代器(含插入更改功能)

实现类

List接口常用的类有 ArrayList 、Vector 和 LinkedList 类

Vector 与 ArrayList 操作是相同的 ,Vector类 过时了 建议使用ArrayList

ArrayList 类

Class ArrayList<E>

java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.ArrayList<E>

ArrayList类 以数组的形式存储 ,该类提供了数组操作的方法!

/*ArrayList类 的定义*/
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess,Cloneable, Serializable

ArrayList类 动态数组

List<E> array = new ArrayList()<E>;
List<E> array = new ArrayList<E>(int max); //设置空间大小

LinkedList 类

Class LinkedList<E>

java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.AbstractSequentialList<E>
java.util.LinkedList<E>

LinkedList类 以双向链表的形式存储数据 ,也一样继承 List类 中的方法!

/*LinkedList的定义*/
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable

LinkedList类 链表结构保存对象

List list = new LinkedList();

Deque接口 拓展 Queue 接口 ,Queue接口实现的方法:

返回类型 方法 说明
boolean add(E e) 添加元素
boolean offer(E e) 添加元素
E element() 获取头元素
E peek() 获取头元素
E poll() 获取并删除头元素
E remove() 获取并删除头元素

方法区别

add 与 offer 区别

offer 在队列有限制的的情况下优势更大!

element 与 peek 区别

element 队列为空,则抛出异常
peek 队列为空,则返回 null

poll 与 remove 区别

poll 队列为空,则返回 null
remove 队列为空,则抛出异常

ArrayList与LinkedList的区别

ArrayList查找较快,但插入、删除对象的速度较慢

LinkedList查找较慢,但插入、删除对象的速度较快

代码示例:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        //另个用法一样
//        List  list = new ArrayList();
        List list = new LinkedList();
        
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        
        //集合长度
        System.out.println("长度为:"+list.size());
        //集合获取索引值(2)
        System.out.println("get(2):"+list.get(2));
        //更该索引值(2)
        list.set(2,"cc");
        //删除索引值4
        list.remove(4);
        //中间插入
        list.add(1,"bb");
        list.add(null);
        
        //输出内容
        System.out.println("遍历集合:");
        for (int i = 0; i < list.size(); i++) {
    
    
            System.out.println(list.get(i));
        }
        
    }
}

/*

长度为:5
get(2):c
遍历集合:
a
bb
b
cc
d
null

*/

Set集合

Set集合中的对象不按特定的方式排序,只是单纯的把对象添加到集合中,但不能有重复对象,Set接口 继承了 Collection接口,因此也有父接口的方法。想要获取 Set集合中的元素,需要获取整个集合与元素遍历,不能更改集合元素值!!

实现类

TreeSet类

Class TreeSet<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E>
java.util.TreeSet<E>

TreeSet类 实现了Set集合在遍历集合时按照自然顺序递增排序

存储的类型如果是 对象,那么该对象就必须实现 Comparable接口 ,Comparable接口中有compareTo抽象方法 可以对集合处理排列方式 ,compareTo抽象方法 返回整型进行控制排列(0(相等) 、 正数 (小于)、 负数(大于))

TreeSet<E> set = new TreeSet<E>();

TreeSet类 提供方法

返回类型 方法 说明
<E> first() 返回集合中当前的第一个(最低)元素
<E> last() 返回集合中当前的最后(最高)元素
<E> comparator() 用于对集合元素进行排序的比较器,以比较返回 null
SortedSet<E> headSet(E toElement) 返回新的Set集合,新集合 toElement(不包含)之前所有对象
SortedSet<E> subSet(E fromElement, E toElement) 返回新的Set集合,其元素的范围从 fromElement (含)到 toElement
SortedSet<E> tailSet(E fromElement) 返回新的Set集合,新集合 fromElement(包含)之后所有对象

代码示例:

import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        TreeSet<Student> set = new TreeSet();
        
        Student s = new Student(1 , 20 , "小明");
        Student s2 = new Student(2 , 22 , "小红");
        Student s3 = new Student(3 , 22 , "小军");
        Student s4 = new Student(4 , 24 , "小张");
        Student s5 = new Student(6 , 21 , "张三");
        Student s6 = new Student(9 , 23 , "李四");
        
        set.add(s);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        set.add(s5);
        set.add(s6);
        set.add(s6);//重复,则无效
//        set.add(null); 异常
    
        System.out.println("集合总数为:"+set.size());
    
        System.out.println("返回集合第一个元素:"+set.first());
        
        System.out.println("返回集合最后一个元素:"+set.last());
    
        System.out.println("排序比较结果为:"+set.comparator());
        
        System.out.println("\n输出集合:");
        Iterator it = set.iterator();
        //如果迭代有更多的元素,则true
        while(it.hasNext()){
    
    
            //next() 会自动调用对象的 toString()方法
            System.out.println(it.next());
        }
        
        //测试headSet()方法
        System.out.println("\n测试方法2");
        //截止至s4对象位置
        it = set.headSet(s4).iterator();
        while(it.hasNext()){
    
    
            //next() 会自动调用对象的 toString()方法
            System.out.println(it.next());
        }
    
        //测试subSet()方法
        System.out.println("\n测试方法3");
        //指定对象范围s ,s3
        it = set.subSet(s , s3).iterator();
        while(it.hasNext()){
    
    
            //next() 会自动调用对象的 toString()方法
            System.out.println(it.next());
        }
    
        //测试stailSet()方法
        System.out.println("\n测试方法4");
        //从s4对象位置开始排序
        it = set.tailSet(s4).iterator();
        while(it.hasNext()){
    
    
            //next() 会自动调用对象的 toString()方法
            System.out.println(it.next());
        }
        
    }
}


class Student implements Comparable<Student>{
    
    
    int id;
    int age;
    String name;
    
    public Student(){
    
    }
    //实例对象时获取的参数(信息)
    public Student(int id , int age , String name){
    
    
        super();
        this.id = id;
        this.age = age;
        this.name = name;
    }
    
    //迭代输出的信息
    @Override
    public String toString() {
    
    
        return "Student{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    
    //迭代排序用到的方法
    @Override
    public int compareTo(Student stt) {
    
    
        /*
        * 排序的值尽可能保持 唯一
        * 如唯一的id
        * */
        
        //按照age排序的顺序(重复会被覆盖掉)
//        int tmp = this.age - stt.age;
        
        //按照id排序的顺序
        int tmp = this.id - stt.id;
        if(tmp != 0){
    
    
            //Math.abs() 绝对值
            //差值 除以本身 绝对值,结果只有 1 或 -1值
            tmp = tmp / Math.abs(tmp);
        }
        /*
        * 判断结果只有 0 、 1 、 -1(相等 ; 小于 ; 大于)
        *   根据三值来进行排序
        * */
        return tmp;
    }
    
    
    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) {
    
    
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
    
    
            return false;
        }
        Student student = (Student) o;
        return id == student.id;
    }
    
    @Override
    public int hashCode() {
    
    
        return Objects.hash(id);
    }
}

/*

集合总数为:5
返回集合第一个元素:Student{id=1, age=20, name='小明'}
返回集合最后一个元素:Student{id=9, age=23, name='李四'}
排序比较结果为:null

输出集合:
Student{id=1, age=20, name='小明'}
Student{id=2, age=22, name='小红'}
Student{id=3, age=22, name='小军'}
Student{id=4, age=24, name='小张'}
Student{id=9, age=23, name='李四'}

测试方法2
Student{id=1, age=20, name='小明'}
Student{id=2, age=22, name='小红'}
Student{id=3, age=22, name='小军'}

测试方法3
Student{id=1, age=20, name='小明'}
Student{id=2, age=22, name='小红'}

测试方法4
Student{id=4, age=24, name='小张'}
Student{id=9, age=23, name='李四'}

*/

HashSet类

Class HashSet<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E> Classs HashSet<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E>

HashSet类 在Set集合中的散列形式存储数据的!

不能更改唯一值否则哈希表的唯一地址会错误,也能被删除!
用HashSet存储的对象,应该重写以下两方法 hashCode()存哈希、equals()方法 哈希地址判断(以上两个方法会影响HashSet唯一性的存储状况)

HashSet集合 可能情况:
集合中不会保存相同的对象
同一个哈希地址可以存放多个不同对象

代码示例:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;

public class Demo2 {
    
    
    public static void main(String[] args) {
    
    
        HashSet<Person> set = new HashSet<>();
        
        Person p1 = new Person(1,"小明");
        Person p2 = new Person(2,"小红");
        Person p3 = new Person(3,"小军");
        Person p4 = new Person(4,"小张");
        Person p5 = new Person(5,"小海");
        Person p6 = new Person(6,"小明");
    
        //首位位置里的id:9
        set.add(p1);
    
        /*p1添加错误是因为该集合唯一性是根据 属性id 进行判断唯一的*/
        //最后位置里的id:9
        p1.id = 9;
        System.out.println("更改p1的id改至9添加是否成功?"+set.add(p1));
    
        /*添加无效*/
        set.add(p1);
        
        set.add(p2);
        set.add(p3);
        set.add(p4);
        set.add(p5);
        set.add(p6);
        
        System.out.println("遍历数组:");
        Iterator<Person> in = set.iterator();
        while (in.hasNext()){
    
    
            System.out.println(in.next());
        }
    }
}

class Person{
    
    
    int id ;
    String name;
    
    public Person(int id , String name){
    
    
        super();
        this.id = id ;
        this.name = name;
    }
    
    //以下两个方法会影响 Set集合 的存储情况
    
    //输出对象的值
    @Override
    public String toString() {
    
    
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
    
    //比较哈希值的地址
    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) {
    
    
            return true;
        }
        if (!(o instanceof Person)) {
    
    
            return false;
        }
        Person person = (Person) o;
        return id == person.id;
    }
    
    //根据hash返回哈希值
    @Override
    public int hashCode() {
    
    
        return Objects.hash(id);
    }
}

/*	运行结果

更改p1的id改至9添加是否成功?true
遍历数组:
Person{id=9, name='小明'}
Person{id=2, name='小红'}
Person{id=3, name='小军'}
Person{id=4, name='小张'}
Person{id=5, name='小海'}
Person{id=6, name='小明'}
Person{id=9, name='小明'}

*/

Map集合

Map集合没有 Collection接口 ,但提供了 key (键)和 value(值)的映射, key (键)和 value(值)都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了 Map 集合之中。Map中不能有相同的 key (键),每个key (键)只能映射一个value(值),key (键)决定了存储对象在映射中的存储位置,

哈希存储原理

注意:

当对象被存进 HashSet 集合后,就不能修改该对象中的那些参与计算的哈希值的属性了,否则,哈希值不对应找不到,在这种情况下,即使在 contains()方法检索对象,也是返回 null 空的结果,这也导致无法从 HashSet 集合中删除当前对象,从而造成内存泄露

Map接口提供的方法

修饰符 方法 说明
V put(K key , V value) 将键值数据存入Map中,如果是覆盖value值,则返回 旧的value ,如果非覆盖,则null
void clear() 清空Map中所有的映射
boolean containsKey(Object key) 查找Map中是否存在指定 键
boolean containsValue(Object value) 查找Map中是否存在指定 值、
V get(Object key) 通过 键,获取 值,找不到则null
Set<K> keySet() 返回集合所有 key (键)对象 形成的Set集合
Collection<V> values() 返回集合所有 value(值)对象 形成的Collection集合
V remove(Object key) 删除指定 Key 的映射,返回删除的 value值 ,没有则null
default V replace(K key , V value) Key映射的值,替换指定键条目
int size() 键 - 值 映射数(集合大小)

实现类

HashMap类

Class HashMap<K,V>

java.lang.Object
java.util.AbstractMap<K,V>
java.util.HashMap<K,V>

HashMap类 散列存储
键值对速度更快,允许null对象,无序存储,但添加、删除快

HashMap<K , V> m = new HashMap<K , V>();
/*构造一个空的 HashMap ,默认初始容量(16)和默认负载因子(0.75)(容量和负载因子是影响效率的因素*/

LinkedHashMap类

Class LinkedHashMap<K,V>

java.lang.Object
java.util.AbstractMap<K,V>
java.util.HashMap<K,V>
java.util.LinkedHashMap<K,V>

LinkedHashMap类 有序存储
存储在 哈希表 和 双向链表 中,两个存储结合使得有序,实现快速查找,高性能,但消耗空间

TreeMap类

Class TreeMap<K,V>

java.lang.Object
java.util.AbstractMap<K,V>
java.util.TreeMap<K,V>

TreeMap类 树状
键值对存放有序,不允许null对象,映射关系有一定的顺序,Key(键)不支持对象,数值,null!!

Hashtable类

Class Hashtable<K,V>

java.lang.Object
java.util.Dictionary<K,V>
java.util.Hashtable<K,V>

Hashtable类 安全线程(又称旧版哈希存储)
组中每个哈希桶的对象是以排队形式执行,得以保证 安全线程 ,但效率低

ConcurrentHashMap类

Class ConcurrentHashMap<K,V>

java.lang.Object
java.util.AbstractMap<K,V>
java.util.concurrent.ConcurrentHashMap<K,V>

ConcurrentHashMap类 安全高效
每个哈希桶同时执行,但哈希桶中的链表是排队形式的(又称 分段锁机制),得以保证 安全线程 和 高效率的情况

实现类的区别

安全方面 多线程 建议使用 Hashtable 、ConcurrentHashMap
有排序要求的 建议使用 LinkedHashMap 、TreeMap
使用操作方面基本一样,少部分用于特殊方面可自行api了解

代码示例:

import java.util.*;

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        HashMap<Object,Object> map = new HashMap<>();
        
        //添加数据
        map.put("no1" , "no1字符串");
        map.put(12 , "12数值");
        map.put(12.3, "12.3单精度");
        map.put(null , null);
        map.put(new Object() , "obj对象");
    
        System.out.println("Map集合总数为:"+ map.size());
    
        System.out.println("在Map集合中 查找12的键 是否存在?"+map.containsKey(12));
    
        System.out.println("在Map集合中 查找 obj的值 是否存在?"+map.containsValue("obj"));
    
        //遍历方式 1
        System.out.println("\n遍历Map集合:");
        System.out.println("遍历key :");
        //以Set集合的形式获取所有对象Key对象
        Set<Object> set = map.keySet();
        for (Object key : set) {
    
    
            //通过 键 获取值 并输出
            System.out.println("key :" + key + "\t\t\tV :" + map.get(key));
        }
        
        System.out.println("\n Collection 遍历value :");
        //以Collection形式获取对象值
        Collection<Object> con = map.values();
        for (Object o : con) {
    
    
            System.out.println(o);
        }
        
        //遍历方式 2
        //也可以这样获取set
        //Map存储的就是Map.Entry , Map.Entry存储了 Key键 和 Value值 
        /*
        
        Set<Map.Entry<Object, Object>> set = map.entrySet();
        Iterator<Map.Entry<Object, Object>> iter = set.iterator();
        while(iter.hasNext()){
        	Map.Entry<Object,Object> me = iter.next();
        	System.out.println(me.getKey()+" --> "+me.getValue());
        }
        */
    }
}

/*

Map集合总数为:5
在Map集合中 查找12的键 是否存在?true
在Map集合中 查找 obj的值 是否存在?false

遍历Map集合:
遍历key :
key :null			V :null
key :java.lang.Object@10f87f48			V :obj对象
key :no1			V :no1字符串
key :12.3			V :12.3单精度
key :12			V :12数值

遍历value :
null
obj对象
no1字符串
12.3单精度
12数值

*/

集合总结

  1. 类集合是一个动态的对象数组,可以向集合中加入任意多的内容
  2. List 接口中是允许有重复元素的,Set 接口中是不允许有重复元素
  3. 所有的重复元素依靠 hashCode()和 equals 进行区分
  4. List 接口的常用子类:ArrayList、Vector
  5. Set 接口的常用子类:HashSet、TreeSet
  6. TreeSet 是可以排序,一个类的对象依靠 Comparable 接口排序
  7. Map 接口中允许存放一对内容,key(键)、value(值)
  8. Map 接口的子类:HashMap、Hashtable、TreeMap
  9. Map 使用 Iterator 输出的详细步骤

迭代器

迭代器用来遍历集合中的元素

Iterator接口

Iterator接口 是 Collection接口 的子接口 用来遍历删除数据用的

方法

返回类型 方法 说明
boolean hasNext() 迭代方向存在元素,则 true
E next() 返回当前指向元素,并前进一位
void remove() 从底层集合中删除此迭代器返回的最后一个元素

ListIterator接口

ListIterator接口 是 List接口 的子接口 用来 遍历 删除 添加使用

方法

返回类型 方法 说明
void add(E e) 将元素插入列表
boolean hasNext() 迭代正方向有元素
E next() 返回列表下一位元素的值
int nextIndex() 返回随后调用 next()元素 的索引
E previous() 返回列表上一位元素的值
int previousInext() 返回随后调用 previous()元素 的索引
void remove() 删除由 next() 或 previous() 返回的元素
void set(E e) 替换 由next() 或 previous() 返回的元素

迭代器异常说明:

快速失败: 迭代器创建后对集合进行批量更改或删除会导致迭代器出现不确定行为,迭代器会抛出ConcurrentModificationException 异常

安全失败: 迭代器在创建时候会备份一份集合,批量修改会导致数据的不确定,没能即使同步导致的异常,迭代器会抛出 ConcurrentModificationException 异常

代码示例: (更多方法可执行测试)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class Demo {
    
    
    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);
        list.add(6);
    
        System.out.println("size : " +list.size());
    
    //   迭代器
        
        Iterator<Integer> ito = list.listIterator();
        while (ito.hasNext()){
    
    
            int n = ito.next();
            if (n == 4){
    
    
                ito.remove();
                continue;
            }
            System.out.print(n+"  ");
        }
        System.out.println("\n======");
        System.out.println(list);
        System.out.println("====================");
    
        System.out.println( );
        System.out.println( );
        
        //此时 list内容 == [1, 2, 3, 5, 6]
        
        ListIterator<Integer> listIto = list.listIterator();
        //向前两位
        listIto.next();
        listIto.next();
        listIto.previous();
        listIto.next();
        listIto.previous();
        System.out.println("迭代器前索引 : "+ listIto.nextIndex());
        
        
        System.out.println("\n======");
        System.out.println(list);
        System.out.println("======");
        
    }
}


/*

size : 6
1  2  3  5  6  
======
[1, 2, 3, 5, 6]
====================


迭代器前索引 : 1

======
[1, 2, 3, 5, 6]
======


*/

猜你喜欢

转载自blog.csdn.net/weixin_45963193/article/details/113822880