java入门篇11 --- 集合

无论什么语言,集合应当是我们最常用的一种类型啦,大体上分为有序列表、map、sey、队列

首先先来看一下有序列表,

List内部跟数组一样也是按照先后顺序排放的,但对于增删,非常方便,list的实现大多是使用ArrayList实现的,先来看一下List的源码,这里面有一个<E>,这个就是泛型,java是面向对象语言,他在运行期间,才会将我们的类进行初始化,因此,就利用这一特性,我们输入指定的引用类型,注意必须是引用类型,基本类型不是类,JVM在编译是,会把它当作object,然后再运行期间就会帮我们转化为指定的类型

// 一个接口
public interface List<E> extends Collection<E> {
    int size();  // 大小
    boolean isEmpty();  // 是否为空
    boolean contains(Object o);  // 是否包含
    Iterator<E> iterator();  // 是否可迭代
    Object[] toArray();  // 转化为数组,返回就是object[]
    <T> T[] toArray(T[] a);  // 转化为数组,返回就是指定的类型[]
    boolean add(E e);  // 增加,返回true,false
    boolean remove(Object o);  // 删除,返回bollean
    boolean containsAll(Collection<?> c);  // 包含
    boolean addAll(Collection<? extends E> c);// 添加
    boolean addAll(int index, Collection<? extends E> c);  // 添加指定位置
    boolean removeAll(Collection<?> c);  // 删除
    @SuppressWarnings({"unchecked", "rawtypes"})
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }  // 拍讯,因此想要排序,必须实现Comparator接口
    void clear(); // 情况
    boolean equals(Object o);  // 判断是否相等
    int hashCode();  // 获取这个code, 返回值int
    E get(int index);// 获取指定下表的对象
    E set(int index, E element);// 重置
    void add(int index, E element);  // 添加
    E remove(int index);  // 删除
    int indexOf(Object o);  // 获取某个对象的下表
    int lastIndexOf(Object o);  // 获取最后一个对象的下表
    @SuppressWarnings("unchecked")  // 这个传入为空,就会报错
    static <E> java.util.List<E> of() {
        return (java.util.List<E>) ImmutableCollections.ListN.EMPTY_LIST;
    }
    static <E> java.util.List<E> of(E e1, E e2, E e3, E e4, E e5) {  // 初始化指定的列表
        return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5);
    }
}

那么,我们来看一下上面提到的一些方法吧

import java.util.*;
import java.util.List;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        // List<int> s  = new ArrayList<>();  // java: 意外的类型 需要: 引用 找到:    int
        List<String> s = new ArrayList<>();
        System.out.println(s.isEmpty());  // true判断是否为空
        s.add("a");  // 添加
        // s.add(1);  //   java: 不兼容的类型: int无法转换为jåava.lang.String, 因为已经指定类型,所以只能传入指定类型的
        s.add("b");
        System.out.println(s.add("c"));  // true
        System.out.println(s.size());  // 3
        System.out.println(s);  // [a, b, c]
        System.out.println(s.contains("a"));  // true 包含关系
        List<String> s1 = List.of("d", "e");  // 这个返回的只是一个只读的List,因为List只是一个接口
        // s1.add("m"); // java.lang.UnsupportedOperationException
        List<String> s2 = List.of("f", "g");
        s.addAll(s1);
        System.out.println(s);  // [a, b, c, d, e] 插入一个对象
        s.addAll(3, s2);
        System.out.println(s);  // [a, b, c, f, g, d, e]  在指定位置插入
        s.remove("f");
        System.out.println(s);  // [a, b, c, g, d, e]  删除
        System.out.println(s.get(1));  // b  获取指定下表的元素
        // String[] m = s.toArray();  // java: 不兼容的类型: java.lang.Object[]无法转换为java.lang.String[],这个是因为不支持的,随意这个运行之前就是object
        String[] m1 = new String[3];
        String[] m2 = new String[9];
        String[] m3 = s.toArray(m1);  // 可以这样转化,List会把值赋值给传入对象,但是如果数字不够长,List会为他生成新的对象
        String[] m4 = s.toArray(m2);  // 可以这样转化,
        for (String i : m1) {
            System.out.println(i);
        }  // null null null 数组不够长,我们应该清楚,数组扩容之后,会生成一个新对象,愿对象就会被清空
        for (String i : m3) {
            System.out.println(i);
        }  // a b c g d e 这个就是被扩容之后的新对象,所以要记得写返回值
        for (String i : m2) {
            System.out.println(i);
        }  // a b c g d e null null null  这个数组定义的够长,所有值是传进去了
        for (String i : m4) {
            System.out.println(i);
        } // a b c g d e null null null
        // 一般情况下,我们肯定希望正好,可以结合上size或者直接为0
        String[] m5 = s.toArray(new String[0]);
        String[] m6 = s.toArray(new String[s.size()]);
        for (String i : m5) {
            System.out.println(i);
        } // a b c g d e 正好
        for (String i : m6) {
            System.out.println(i);
        } // a b c g d e 正好
        // List 的循环,for each的方法帮助我们封装了迭代,原始应该是这样的
        for (Iterator<String> ss = s.iterator(); ss.hasNext(); ) {
            String sss = ss.next();
            System.out.println(sss);
        } // a b c g d e
        // 如上面所说,for帮助我们做了封装,所以可以直接这样写
        for (String ss : s) {
            System.out.println(ss);
        } // a b c g d e
    }
}

说完了List,接下来我们来看一下map,map是key-value的一种数据结构,他的方法类是HashMap,源码感觉差不太多,都是一些接口定义了一些方法,我们直接看使用,map的实现方法是它使用一个比较大的数组,来存储所有的value,然后计算key的值(hascode),然后根据这个作为索引,将value存储在指定位置,因此如果想用使用自己的类来dingyikey,必须实现equalshenhascode这两个方法,因为这个不常用,就不再展示,但我们既然知道这个原理,就能想象的到map是无序的。接下来看一下map的常用操作

import java.util.*;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        Map<String, Integer> s = new HashMap<>();
        s.put("a", 1);  // 添加
        s.put("b", 2);
        s.put("c", 3);
        System.out.println(s.put("d", 4)); // null
        System.out.println(s.put("d", 4)); // 4  如果重复,就会把原来的值替换,并抛出原来的值
        System.out.println(s.get("a"));  // 1 获取
        System.out.println(s.containsKey("f"));  // false
        System.out.println(s.getOrDefault("f", 1));  // 1 如果获取失败,就会给出默认值
        System.out.println(s.remove("f"));  // null  删除
        System.out.println(s.remove("a"));  // 1  如果有删除,就会返回值
        // 循环所有的key
        for (String key : s.keySet()) {
            System.out.println(key);
            System.out.println(s.get(key));
        }
        // 循环所有的key-value
        for (Map.Entry<String, Integer> ss : s.entrySet()) {
            System.out.println(ss);
            System.out.println(ss.getKey() + ss.getValue());
        }
    }
}

另外还有一种实现map的方法是枚举map,如果对于一些比较少的,可以直接使用这个类型,它并非使用key的code进行安排缩阴的,而是直接使用key,因此效率会更高

import java.time.DayOfWeek;
import java.util.*;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
        map.put(DayOfWeek.MONDAY, "星期一");
        map.put(DayOfWeek.WEDNESDAY, "星期二");
        map.put(DayOfWeek.FRIDAY, "星期五");
        System.out.println(map.get(DayOfWeek.FRIDAY));  // 星期五
        System.out.println(map);  // {MONDAY=星期一, WEDNESDAY=星期二, FRIDAY=星期五}
    }
}

既然上面提到的map是无序的,就如同python中dict是无序的,但还是有有序字典OrderDict,因此java也肯定有有序map,他实现的接口是SortMap, 实现类是TreeMap,但是如果要使用自己的定义的类作为key,请记得要实现compare方法

import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        SortedMap<String, Integer> s = new TreeMap<>(); // 初始化一个有序map
        s.put("a", 1);  // 添加
        s.put("b", 2);
        s.put("c", 3);
        System.out.println(s);  // {a=1, b=2, c=3}
        // 循环打印
        for (Map.Entry<String, Integer> ss : s.entrySet()) {
            System.out.println(ss);
        }  // a=1 b=2 c=3
        // 初始化一个带我们自定义排序方法的有序map,查看结构跟上面相反
        SortedMap<String, Integer> s1 = new TreeMap<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return -o1.compareTo(o2);
            }
        });
        s1.put("a", 1);
        s1.put("b", 2);
        s1.put("c", 3);
        System.out.println(s1);  // {c=3, b=2, a=1}
        for (Map.Entry<String, Integer> ss : s1.entrySet()) {
            System.out.println(ss);
        }  // c=3 b=2 a=1
    }
}

然后我们再来看一下set,集合,他最出名的不就是去重,他有两个实现类,一个是HashSet无序,另外一个是TreeSet有序,而有序集合里面的元素如果没有实现compaer接口,需要初始化时传入一个自定义的比较,跟有序集合类似,就不再赘述

import java.util.*;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        // 无序集合
        Set<String> s = new HashSet<>();
        s.add("c");
        s.add("b");  // 添加
        s.add("a");
        System.out.println(s);  // [a, b, c]
        System.out.println(s.contains("a"));  // true  包含
        System.out.println(s.remove("d"));  // false  删除,没有删除,返回false
        System.out.println(s.remove("a"));  // true
        System.out.println(s);  // [b, c]
        for (String ss : s) {
            System.out.println(ss);
        }  // b c
        // 有序集合
        Set<String> s1 = new TreeSet<>();
        s.add("a");
        s.add("b");
        s.add("c");
        System.out.println(s);  // [a, b, c]
        for (String ss : s) {
            System.out.println(ss);
        }  // a b c
    }
}

接下来看一下队列,队列分为几种,一个是FIFO--Queue,另外一个是有优先级的 PriorityQueue,而这个是必须实现compaer接口,否则没办法选择优先级, 还有双端队列 -- Deque

我们来看一下实例

import java.util.*;

public class HelloWorld {
    public static void main(String[] args) throws Exception {
        // FIFO队尾进,队首出
        Queue<String> q1 = new LinkedList<>();  // 这个 LinkedList 也实现了List接口,根据需要选择,这个就是链表,但是作为List来用,没有特许需求的话,有点吗,他是按照链表来存储的
        // 两端均可进可出
        Deque<String> q2 = new LinkedList<>();
        // 根据默认的或自定义对比方法,出队列,String有compaer方法,我只是自定义了一个反序的
        Queue<String> q3 = new PriorityQueue<>(new Comparator<String>() {
            public int compare(String a, String b) {
                return -a.compareTo(b);
            }
        });
        // add 添加方法,如果添加失败就报错,比如队列满了
        // FIFO添加对象方法
        q1.add("a");
        q1.add("b");
        q1.add("c");
        System.out.println(q1);  // [a, b, c]
        for (String qq : q1) {
            System.out.println(qq);
        }  // a b c
        // 双端队列添加方法
        q2.addFirst("a");
        q2.addFirst("b");
        q2.addLast("d");
        q2.addLast("c");
        System.out.println(q2);  // [b, a, d, c]
        for (String qq : q2) {
            System.out.println(qq);
        }  // b a d c
        // 优先级队列添加方法
        q3.add("a");
        q3.add("b");
        q3.add("c");
        System.out.println(q3);  // [c, a, b]
        for (String qq : q3) {
            System.out.println(qq);
        }  // c a b
        // offer 添加方法,会返回boolean值,告知是否添加成功,不会报错
        q1.offer("a");
        q1.offer("b");
        System.out.println(q1.offer("c"));  // true
        System.out.println(q1);  // [a, b, c, a, b, c]
        for (String qq : q1) {
            System.out.println(qq);
        }  // a b c
        // 双端队列添加方法
        q2.offerFirst("a");
        q2.offerFirst("b");
        q2.offerLast("d");
        q2.offerLast("c");
        System.out.println(q2);  // [b, a, b, a, d, c, d, c]
        for (String qq : q2) {
            System.out.println(qq);
        }  // b a b a d c d c
        // 优先级队列添加方法
        q3.offer("a");
        q3.offer("b");
        q3.offer("c");
        System.out.println(q3);  // [c, b, c, a, a, b]
        for (String qq : q3) {
            System.out.println(qq);
        }  // c b c a a b
        // 获取首元素并删除
        // 删除失败会报错
        Queue<Integer> i = new LinkedList<>();
        // i.remove();  // java.util.NoSuchElementException
        q1.remove();
        System.out.println(q1);  // [b, c, a, b, c]
        q2.removeFirst();
        System.out.println(q2);  // [a, b, a, d, c, d, c]
        q2.removeLast();
        System.out.println(q2);  // [a, b, a, d, c, d]
        q3.remove();
        System.out.println(q3);  // [c, b, b, a, a]
        // 删除失败不会报错,会返回false 或者 null
        System.out.println(i.poll());
        System.out.println(q1.poll());  // b
        System.out.println(q1);  // [c, a, b, c]
        q2.pollFirst();
        System.out.println(q2);  // [b, a, d, c, d]
        q2.pollLast();
        System.out.println(q2);  // [b, a, d, c]
        q3.poll();
        System.out.println(q3);  // [b, a, b, a]
        // 取首元素但是不会删除
        // 删除失败会报错
        System.out.println("----");
        // i.element();  // java.util.NoSuchElementException
        System.out.println(q1.element());  // c
        System.out.println(q1);  // [c, a, b, c]
        q1.element();
        System.out.println(q1);  // [c, a, b, c]
        q2.getFirst();
        System.out.println(q2);  // [b, a, d, c]
        System.out.println(q2.getFirst());  // b
        System.out.println(q2);  // [b, a, d, c]
        System.out.println(q2.getLast());  // c
        System.out.println(q2);  // [b, a, d, c]
        q2.getLast();
        System.out.println(q2);  // [b, a, d, c]
        q3.element();
        System.out.println(q3);  // [b, a, b, a]
        q3.element();
        System.out.println(q3);  // [b, a, b, a]
        // 删除失败不会报错,会返回false 或者 null
        System.out.println(i.peek());  // null
        System.out.println(q1.peek());  // c
        System.out.println(q1);  // [c, a, b, c]
        System.out.println(q1.peek());  // c
        System.out.println(q1);  // [c, a, b, c]
        System.out.println(q2.peekFirst());  // b
        System.out.println(q2);  // [b, a, d, c]
        q2.peekFirst();
        System.out.println(q2);  // [b, a, d, c]
        System.out.println(q2.peekLast());  // c
        System.out.println(q2);  // [b, a, d, c]
        q2.peekLast();
        System.out.println(q2);  // [b, a, d, c]
        q3.peek();
        System.out.println(q3);  // [b, a, b, a]
        q3.peek();
        System.out.println(q3);  // [b, a, b, a]

        // 双端队列还有 pop 的 取出队首且删除的犯法,但取不到就会报错
        System.out.println(q2.pop());  // b
        System.out.println(q2.pop());  // a
        System.out.println(q2.pop());  // d
        System.out.println(q2.pop());  // c
        //System.out.println(q2.pop());  // java.util.NoSuchElementException
        // 还有push增加方法
        q2.push("a");
        q2.push("b");
        System.out.println(q2);
        // 因此我们可以使用Deque来模拟栈
        Deque<String> q4 = new LinkedList<>();
        // 模拟压栈
        q4.push("a");
        q4.push("b");
        // 模拟出栈
        System.out.println(q4); // [b, a]
        System.out.println(q4.pop());  // b 删除队首,并跑操
        System.out.println(q4);  // [a]
        System.out.println(q4.peek()); // a 只取队首的值,不删除
        System.out.println(q4); // [a]
    }
}

我们确实是可以使用Deque来模拟栈的

猜你喜欢

转载自www.cnblogs.com/yangshixiong/p/12172253.html