【JavaSE】之集合类

目录

1.Java类集简介

1.1Collection接口

2.List接口-允许元素重复

2.1ArrayList(优先考虑)

2.2Vector

2.3ArrayList与Victor的区别:

2.4LinkedList-基于链表实现的动态数组

3.Set接口-不允许重复元素(Set接口就是value值相同的Map接口,先有Map才有Set)

3.1HashSet-无序存储

3.2TreeSet-有序存储-底层基于红黑树-(TreeMap)

3.3TreeSet排序分析

3.4重复元素判断

4.集合输出(迭代器输出)

4.1迭代输出:Iterator(只有Collection接口有)--只能从前向后输出(Collection接口提供)

4.2双向迭代接口:ListIterator

4.3Enumeration(JDK1.0)枚举输出-Vector类支持

4.4foreach输出

5.Map集合

5.1HashMap子类(常用)

5.2Hashtable

5.3HashMap与Hashtable的区别:

5.4Map集合使用迭代器(Iterator)输出

6.栈与队列

6.1栈(Stack):先入后出

6.2队列(Queue)--先进先出

7.资源文件操作(Properties属性文件)

8.工具类

8.1将线程不安全集合包装为线程安全集合

8.2集合排序

8.3集合反转

9.Stream数据流(JDK1.8新增)

9.1Collection接口改进

9.2Stream操作


1.Java类集简介

        类集实际上就属于动态数组,因为数组本身有一个最大的缺陷(长度是固定的)。从JDK1.2开始,Java提供了动态的对象数组实现框架--Java类集框架。

1.1Collection接口

Collection接口的定义如下:

public interface Collection<E> extends Iterable<E>

此接口常用的方法有如下几个:

public boolean add(E e)  //向集合中添加数据
public boolean addAll(Collection<? extends E> c)  //向集合中添加一组数据
public void clear()  //清空集合数据
public boolean contains(Object o)  //查找数据是否存在,需要使用equals()方法
public boolean remove(Object o)  //删除数据,需要equals()方法
public int size()  //取得集合长度
public Object[] toArray()  //将集合变为对象数据返回
public Iterator<E> iterator()  //取得Iterator接口对象,用于集合输出

        Collection接口无法判断元素是否重复,所以经常使用它的子接口。Collection接口有两个使用频率很高的子接口List(允许重复元素)、Set(不允许重复元素)

2.List接口-允许元素重复

List接口中重要的扩展方法:

public E get(int index):根据索引取得数据
public E set(int index, E element):根据指定索引修改相应元素

2.1ArrayList(优先考虑)

接口中保存自定义类对象,自定义类必须覆写equals()方法;因为类集contains()、remove()等方法需要调用equals()来判断元素是否相等。

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
        if(obj == null){
            return false;
        }
        if(!(obj instanceof Person)){
            return false;
        }
        if(this == obj){
            return true;
        }
        Person per = (Person) obj;
        return this.age == per.age && this.name.equals(per.name);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test2{
    public static void main(String[] args) throws Exception {
        Person per1 = new Person("张三",18);
        Person per2 = new Person("李四",20);
        Person per3 = new Person("王五",40);
        List<Person> list = new ArrayList<>();
        list.add(per1);
        list.add(per2);
        list.add(per3);
        System.out.println(list.contains(new Person("李四",20)));
        list.remove(new Person("王五",40));
        System.out.println(list);
    }
}

        底层实现是一个对象数组,声明一个ArrayList对象时,长度为0,当数组长度不够用时,扩容策略变为原来数组的1.5倍,对集合增删改查都是异步处理,性能较高,线程不安全。

2.2Vector

        底层实现是一个对象数组,声明一个Victor对象时,初始化对象数组为10,当数组长度不够用时,扩容策略变为原来数组的2倍,对集合的修改都采用同步处理(直接在方法上使用内建锁),性能较低,线程安全。

2.3ArrayList与Victor的区别:

I.产生版本:

        Vector是JDK1.0,ArrayList是JDK1.2产生

II.线程安全:

        Vector采用在方法上添加synchronized来保证线程安全,性能较低;ArrayList采用异步处理,性能较高,线程不安全。

III.初始化以及扩容策略:

        Vector对象产生时就初始化数组大小为10,当数组长度不够用时,扩容为源数组的2倍;ArrayList使用懒加载策略,在第一次添加元素时才初始化数组大小,当数组不够用时,扩容策略为源数组的1.5倍。

2.4LinkedList-基于链表实现的动态数组

        大量访问数据用数组,大量有数据插入、修改用链表

3.Set接口-不允许重复元素(Set接口就是value值相同的Map接口,先有Map才有Set)

        Set接口并没有对Collection接口进行扩充,方法与Collection完全一致,因此,在Set接口中没有get()方法。常用两个子类HashSet、TreeSet

3.1HashSet-无序存储

(根据对象的哈希码计算,但是哈希码我们不知道,所以为无序存储)

不允许元素重复,并且无序存储(根据hash码来保存元素),允许存放null。

public class Test2{
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("hello");
        set.add("hello");
        set.add("world");
        set.add("java");
        set.add(null);
        System.out.println(set);
    }
}

3.2TreeSet-有序存储-底层基于红黑树-(TreeMap)

不允许元素重复,并且按照升序排序,不允许存放null。

public class Test2{
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("hello");
        set.add("hello");
        set.add("world");
        set.add("java");
        set.add(null);
        System.out.println(set);
    }
}

public class Test2{
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("hello");
        set.add("hello");
        set.add("world");
        set.add("java");
        System.out.println(set);
    }
}

3.3TreeSet排序分析

如果要进行对象数组的排序,对象所在的类一定要实现Compareable接口并且覆写compareTo()方法。

public int compareTo(T o)

返回值三种情况:

返回正数:表示当前对象大于目标对象

返回0:表示当前对象等于目标对象

返回负数:表示当前对象小于目标对象

class Person implements Comparable<Person>{
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        return this.age-o.age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test2{
    public static void main(String[] args) {
        Person person1 = new Person();
        person1.setAge(22);
        person1.setName("Jack");
        Person person2 = new Person();
        person2.setAge(23);
        person2.setName("Tom");
        Person person3 = new Person();
        person3.setAge(18);
        person3.setName("Alex");
        Set<Person> set = new TreeSet<>();
        set.add(person1);
        set.add(person2);
        set.add(person3);
        System.out.println(set);
    }
}

3.4重复元素判断

        在使用TreeSet子类进行数据保存的时候,重复元素的判断依靠的Comparable接口完成的。但是这并不是全部Set接口判断重复元素的方式,因为如果使用的是HashSet子类,由于其跟Comparable没有任何关系,所以它判断重复元素的方式依靠的是Object类中的两个方法:

//hash码
public native int hashCode()
//对象比较
public boolean equals(Object obj)

例:覆写hashCode()与equals()方法消除重复

class Person implements Comparable<Person> {
    private String name ;
    private Integer age ;
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) 
            return true;
        if (o == null || getClass() != o.getClass()) 
            return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) && Objects.equals(age, person.age);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    @Override
    public int compareTo(Person o) {
        if (this.age > o.age ) {
            return 1 ;
        }else if (this.age < o.age ){
            return -1 ;
        }else {
            return this.name.compareTo(o.name) ;
        }
    }
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

public class Test {
    public static void main(String[] args) {
        Set<Person> set = new HashSet<>() ;
        set.add(new Person("张三",20)) ;
        // 重复元素
        set.add(new Person("张三",20)) ;
        set.add(new Person("李四",20)) ;
        set.add(new Person("王五",19)) ;
        System.out.println(set) ;
    }
}

        注意:若两个对象equals方法返回true,它们的hashCode必然要保证相等。但是两个对象的hashCode相等,equals不一定相等。当且仅当equals与hashCode方法均返回true,才认为两个对象真正相等。

4.集合输出(迭代器输出)

迭代器:为了遍历集合而生。

         在之前进行集合输出的时候都利用了toString(),或者利用了List接口中的get()方法。这些都不是集合标准的输出。集合输 出一共有四种手段:Iterator、ListIterator、Enumeration、foreach

4.1迭代输出:Iterator(只有Collection接口有)--只能从前向后输出(Collection接口提供)

对于Iterator接口最初的设计里面实际有三个抽象方法:

//1.判断是否有下一个元素
public boolean hasNext()
//2.取得当前元素
public E next()
//3.删除元素(此方法从JDK1.8开始变为default完整方法)
public default void remove()

例:

public class Test3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("bit");
        list.add("hello");
        list.add("java");
        //取得ArrayList的迭代器
        Iterator<String> iterator = list.iterator() ; // 实例化Iterator对象
        while (iterator.hasNext()) {
            System.out.println(iterator.next()) ;
        }
    }
}

fail-fast机制:

public class Test3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"A","B","C","D");
        //modCount = 6
        //取得集合迭代器(取得当前集合的副本)
        //new Itr();
        //expectedModCount = 6
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){
            //调用checkForComodification检查当前副本中的expectedModCount是否等于
            //集合的modCount
            String str = iterator.next();
            if(str.equals("B")){
                //调用集合类提供的remove()
                //modCount = 7
                list.remove("B");
                continue;
            }
            System.out.println(str);
        }
    }
}

如果将其改为:

if(str.equals("B")){
    iterator.remove();
    continue;
}

        ConcurrentModificationException发生在Collection集合使用迭代器遍历时,使用了集合类提供的修改集合内容而报错。而如果使用Iterator迭代器的remove()不会出现错误。

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

        Collection集合中modCount表示当前集合修改的次数,expectedModCount是迭代器中记录当前集合的修改次数。

        当取得集合迭代器时(即调用list.iterator()),expectedModCount = modCount,换言之,迭代器就是当前集合的一个副本。

         快速失败策略保证了所有用户在进行迭代遍历集合时,拿到的数据一定是最新的数据。(避免“脏读”产生)。

         总结:以后在迭代器遍历时,不要修改集合内容

4.2双向迭代接口:ListIterator

此接口定义的方法:

//1.判断是否有上一个元素
public boolean hasPrevious()
//2.取得上一个元素
public E previous()

Iterator接口对象是由Collection接口支持的,但是ListIterator是由List接口支持的,List接口提供有如下方法:

//取得ListIterator接口对象
public ListIterator listIterator()

例:

public class Test3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("a");
        list.add("hello");
        list.add("java");
        ListIterator<String> listiterator = list.listIterator() ;
        System.out.println("从前向后输出:");
        while(listiterator.hasNext()){
            System.out.print(listiterator.next()+" ");
        }
        System.out.println("\n从后向前输出:");
        while(listiterator.hasPrevious()){
            System.out.print(listiterator.previous()+" ");
        }
    }
}

注意:如果要想实现由后向前的输出,那么首先应该从前向后的输出,否则无法实现双向

4.3Enumeration(JDK1.0)枚举输出-Vector类支持

观察此接口定义的方法:

//判断是否有下一个元素
public boolean hasMoreElements()
//取得元素
public E nextElement()

        但是要想取得这个接口的实例化对象,是不能依靠Collection、List、Set等接口的。只能够依靠Vector子类,因为Enumeration最早的设计就是为Vector服务的,在Vector类中提供有一个取得Enumeration接口对象的方法:

//取得Enumeration接口对象:
public Enumeration elements()
public class Test3 {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("hello");
        vector.add("a");
        vector.add("hello");
        vector.add("java");
        Enumeration<String> enumeration = vector.elements();
        while(enumeration.hasMoreElements()){
            System.out.println(enumeration.nextElement());
        }
    }
}

4.4foreach输出

从JDK1.5开始foreach可以输出数组,能使用foreach输出的本质在于各个集合类都内置了迭代器

例:

public class Test3 {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("hello");
        vector.add("a");
        vector.add("hello");
        vector.add("java");
        for(String str : vector){
            System.out.println(str);
        }
    }
}

5.Map集合

Map接口是Java中保存二元偶对象(键值对)的最顶层接口

//key值唯一,通过一个key值一定能唯一找到一个value值。
public interface Map<K,V>

Map接口的核心方法:

public V put(K key, V value)  //向Map中添加数据
public V get(K key)  //根据指定的key值取得相应的value值,若没有此key值,返回null
public Set<Map.Entry<K,V>> entrySet()  //将Map集合变为Set集合。
public Set<K> keySet()  //返回所有key值集合,key不能重复
public Collection<V> values()  //返回所有value值,value可以重复

        Map本身是一个接口,要使用Map需要通过子类进行对象实例化。Map接口的常用子类有如下四个:HashMap、Hashtable、TreeMap、ConcurrentHashMap。

5.1HashMap子类(常用)

例:

public class Test3 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"hello");
        //当key值重复,再次put变为相应的value的更新操作
        map.put(1,"Hello");
        map.put(2,"java");
        System.out.println(map.get(1));
        System.out.println(map.get(99));
    }
}

例:取得Map中所有key信息

public class Test3 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"hello");
        //当key值重复,再次put变为相应的value的更新操作
        map.put(1,"Hello");
        map.put(2,"java");
        map.put(3,"world");
        Set<Integer> set = map.keySet();
        Iterator<Integer> iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

总结:(HashMap类比HashSet)

1.允许key和value为null,且key值有且只有一个为空,value可以有任意多个为null

2.JDK1.2产生

3.异步处理,效率高,线程不安全

4.底层哈希表+红黑树(JDK8)

5.2Hashtable

JDK1.0提供有三大主要类:Vector、Enumeration、Hashtable。Hashtable是最早实现这种二元偶数据结构。

例:

public class Test3 {
    public static void main(String[] args) {
        Map<Integer,String> map = new Hashtable<>();
        map.put(1,"hello");
        //当key值重复,再次put变为相应的value的更新操作
        map.put(1,"Hello");
        map.put(2,"java");
        map.put(3,"world");
        System.out.println(map);
    }
}

总结:

1.key值与value均不为null

2.JDK1.0产生

3.使用方法上加锁,效率低,线程安全

4.底层哈希表

5.3HashMap与Hashtable的区别:

NO区别HashMapHashtable
1退出版本JDK1.2JDK1.0
2性能异步处理,性能高同步处理、性能较低
3安全性非线程安全线程安全
4null操作允许存放null(有且只有一个)key与value都不为空,否则出现NullPointerException

5.4Map集合使用迭代器(Iterator)输出

public Set<Map.Entry<K,V>> entrySet()  //将Map集合变为Set集合
public class Test3 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"hello");
        map.put(2,"java");
        map.put(3,"world");
        //1.将Map集合变为Set集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //2.获取Iterator对象
        Iterator<Map.Entry<Integer,String>> iterator = set.iterator();
        //3.输出
        while(iterator.hasNext()){
            //4.取出每一个Map.Entry对象
            Map.Entry<Integer,String> entry = iterator.next();
            //5.取得key与value
            System.out.println(entry.getKey()+"="+entry.getValue());
        }
    }
}

6.栈与队列

6.1栈(Stack):先入后出

栈的实现:

class Stack<E> extends Vector<E>  //所以是动态数组

一些方法如下:

push()  //入栈
pop()  //出栈
peek()  //返回栈顶元素但不出栈
public class Test3 {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<>();
        stack.push("A");
        stack.push("B");
        stack.push("C");
        System.out.println(stack.peek());
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        //此时栈已经空
        System.out.println(stack.pop());
    }
}

注意:如果栈已经空了,再次出栈就会空栈异常

6.2队列(Queue)--先进先出

Queue接口有一个子类LinkedList,在这个接口中有如下方法:

public E poll()  //按照队列取出内容

例:

public class Test3 {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        queue.add("A");
        queue.add("B");
        queue.add("C");
        System.out.println(queue.peek());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        //取完数据继续poll返回null
        System.out.println(queue.poll());
    }
}

7.资源文件操作(Properties属性文件)

class Properties extends Hashtable<Object,Object>

        资源文件内容都是k-v格式,并且无论是key、value都是String类型,在进行属性操作的时候往往会使用Properties类提供的方法完成:

//设置属性
public synchronized Object setProperty(String key, String value)
//取得属性,如果没有指定的key,则返回null
public String getProperty(String key)
//取得属性,如果没有指定的key则返回默认值
public String getProperty(String key, String defaultValue)

例:观察属性操作

public class Test3 {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.setProperty("shanxi","xi'an");
        properties.setProperty("sh","shanghai");
        System.out.println(properties.getProperty("shanxi"));
        System.out.println(properties.get("bj"));
        System.out.println(properties.getProperty("bj","beijing"));
    }
}

在Properties类中提供有IO支持的方法:

//保存属性
public void store(OutputStream out, String comments) throws IOException
//读取属性
public synchronized void load(InputStream in) throws IOException

注意:Properties只能操作String

8.工具类

Collections是一个集合操作的工具类,包含有集合反转、排序等操作

8.1将线程不安全集合包装为线程安全集合

        在add、remove等修改方法中使用同步代码块保证线程安全,效率较低。要使用线程安全集合,推荐使用juc包下的并发集合类(ConcurrentHashMap、CopyOnWriterArrayList)

8.2集合排序

Collections.sort(集合名称)

8.3集合反转

Collections.reverse(集合反转)

例:

public class Test3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"A","C","B");
        //将集合转为线程安全集合
        List<String> listSafe = Collections.synchronizedList(list);
        //集合排序
        Collections.sort(listSafe);
        for(String str : listSafe){
            System.out.println(str);
        }
        System.out.println("-------------");
        Collections.reverse(listSafe);
        for(String str : listSafe){
            System.out.println(str);
        }
    }
}

9.Stream数据流(JDK1.8新增)

9.1Collection接口改进

从JDK1.8开始,Collection接口里面除了定义一些抽象方法外,也提供了一些普通方法:

//1.forEach()输出支持
default void forEach(Consumer<? super T>action)
//2.取得Stream数据流对象
default Stream stream()

例:使用forEach输出

public class Test3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1,3,5,7,9);
        list.forEach(System.out::println);
    }
}

9.2Stream操作

例:filter数据过滤

public class Test3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1,3,5,7,9);
        //取得Stream对象
        Stream<Integer> stream = list.stream();
        //统计出集合中所有偶数个数
        System.out.println(stream.filter(e -> e % 2 == 0).count());
    }
}

例:取得最大最小值:max/min

public class Test3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1,3,5,7,9);
        //取得Stream对象
        Stream<Integer> stream = list.stream();
        //统计出集合中最大的数
        System.out.println(stream.max(Integer::compareTo).get());
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gx1500291

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值