集合(Java)

Java集合简介

数组的限制:

  • 数组初始化后大小不可取
  • 数组只能按索引顺序存取

Java.util.Collection

除Map外的所有其他集合类的根接口。java.util包主要提供三种类型的集合:List、Set、Map

  • List 一种有序列表的的集合,按索引排列
  • Set一种没有重复元素的集合
  • Map一种通过键值key-value查找的映射表集合
  • Java集合的设计有几个特点:一是实现了接口和实现类相分离,例如有序表的接口是List,具体的实现类有ArrayList、linkList,二是支持泛型,限制在一个集合中只能放入同一种数据类型的元素
  • Java访问集合通过统一的方式迭代器Iterator来实现。

使用List

ArrayList把添加和删除的操作封装起来,本质是数组。对List的操作,相当于对数组的操作。

List接口,几个主要接口方法:

  • 末尾添加一个元素,void add(E e)
  • 指定索引添加一个元素 void add(int index,E e)
  • 删除指定索引的元素 int remove(int inde)
  • 删除某个元素 int remove(Object e)
  • 获取指定索引的元素 E get(int index)
  • 获取链表大小 int size()

LinkedList(链表),也实现了List接口。

Array LinkedList
获取指定元素 速度很快 需要从头开始查找元素
添加元素到末尾 速度很快 速度很快
在指定位置添加/删除 需要移动元素 不需要移动元素
内存占用 较大
  • List允许添加null
  • 用迭代器Iterator访问List
  • Iterator对象有两个方法:boolean hasNext()是否有下一个元素 E next()返回下一个元素
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

List和Array之间的转换

  • toArray(T[])方法的泛型参数并不是List接口定义的泛型参数,所以,我们实际上可以传入其他类型的数组,例如我们传入Number类型的数组,返回的仍然是Number类型
  • 如果我们传入类型不匹配的数组,例如,String[]类型的数组,由于List的元素是Integer,所以无法放入String数组,这个方法会抛出ArrayStoreException。
  • 将Array变为List
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);

编写equals方法

  • List内部并不是通过==判断两个元素是否相等,而是使用equals()
  • 要使用List的contains()必须要覆写equals()方法
  • equals()方法要满足几个特性:自反性,对称性,传递性,一致性
public boolean equals(Object o) {
    if (o instanceof Person) {
        Person p = (Person) o;
        return this.name.equals(p.name) && this.age == p.age;
    }
    return false;
}//覆写equals 字符串用equals 数值用==

使用Map

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 99);
        Map<String, Student> map = new HashMap<>();
        map.put("Xiao Ming", s); // 将"Xiao Ming"和Student实例映射并关联
        Student target = map.get("Xiao Ming"); // 通过key查找并返回映射的Student实例
        System.out.println(target == s); // true,同一个实例
        System.out.println(target.score); // 99
        Student another = map.get("Bob"); // 通过另一个key查找
        System.out.println(another); // 未找到返回null
    }
}

class Student {
    public String name;
    public int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

  • Map<K,V>是一种键值映射表,put放入map,get 获取对应的value,containsKey()判断是否有这个Key
  • Map也是一个接口,常用实现的类时HashMap
  • 重复放入key-value不会有问题,但是一个key只能有一个value,会覆盖。
  • key不能重复,value可以重复

遍历Map

  • map.keyset() 键的值域
  • Map遍历顺序不一定,随机的

编写equals和hashCode

  • HashMap内部的数组长度总是2的次方
  • 我们覆写equals()和hashCode()时一定要确定两个对象相等,他们的hashCode要相等
  • 两个对象不相等,hascode尽量不相等
  • 通过key计算索引的方式就是调用key对象的hashCode()方法,返回一个int整数。HashMap通过这个方法直接定位Key对应的value索引,继而返回value

使用TreeMap

Map接口有两个实现类,一个是HashMap,另一个是TreeMap。HashMap内部的Key是无序的,而SortMap接口的实现类TreeMap内部Key是有序的。

  • HashMap是一种以空间换时间的映射表,SortMap会对KEY排序,实现类是TreeMAP
  • 使用TreeMap,放入的Key必须实现Comparable接口,定义比较规则

使用Properties

编写程序时,需要读写配置文件,一般Key-Value是String-String类型的。Java集合库提供了一个Properties来表示配置。本质是一个Hashtable。

  • 配置文件信息
  • 以.properties为扩展名,以#为开头
# setting.properties

last_open_file=/data/hello.txt
auto_save_interval=60
String f = "setting.properties";
Properties props = new Properties();
props.load(new java.io.FileInputStream(f));

String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");
  • 读取配置文件一共有三步
  • 创建Properties实例
  • 调用load()读取文件
  • 调用getProperty获取配置
  • InputStream和Reader的区别是一个是字节流,一个是字符流。
  • 字符流用CHAR表示,不涉及编码问题,UTF-8

使用Set

  • 用于存储不重复的元素集合。
  • 添加元素 add()
  • 删除元素 remove()
  • 判断是否包含元素 contains()

Set的实现类HashSet和TreeSet,而HashSet是对HashMap的一个封装。同样的,HashSet无序,TreeSet有序。

使用Queue

  • 通过add()/offer()将元素添加到队尾
  • 通过remove/poll取首元素并删除
  • 通过element/peek获取首元素但不删除

PriorityQueue优先队列

  • 放入优先队列的元素,必须实现Comparable接口,队列会根据排序顺序决定优先级
public class Main {
    public static void main(String[] args) {
        Queue<User> q = new PriorityQueue<>(new UserComparator());
        // 添加3个元素到队列:
        q.offer(new User("Bob", "A1"));
        q.offer(new User("Alice", "A2"));
        q.offer(new User("Boss", "V1"));
        System.out.println(q.poll()); // Boss/V1
        System.out.println(q.poll()); // Bob/A1
        System.out.println(q.poll()); // Alice/A2
        System.out.println(q.poll()); // null,因为队列为空
    }
}

class UserComparator implements Comparator<User> {
    public int compare(User u1, User u2) {
        if (u1.number.charAt(0) == u2.number.charAt(0)) {
            // 如果两人的号都是A开头或者都是V开头,比较号的大小:
            return u1.number.compareTo(u2.number);
        }
        if (u1.number.charAt(0) == 'V') {
            // u1的号码是V开头,优先级高:
            return -1;
        } else {
            return 1;
        }
    }
}

class User {
    public final String name;
    public final String number;

    public User(String name, String number) {
        this.name = name;
        this.number = number;
    }

    public String toString() {
        return name + "/" + number;
    }
}
//比较比较小的先出

使用Deque

  • 允许两头都进,两头都出。双端队列(Double Ended Queue) 学名Deque
  • Java集合提供了接口Deque实现一个双端队列
Queue Deque
添加元素到队尾 add(E e) / offer(E e) addLast(E e) / offerLast(E e)
取队首元素并删除 E remove() / E poll() E removeFirst() / E pollFirst()
取队首元素但不删除 E element() / E peek() E getFirst() / E peekFirst()
添加元素到队首 addFirst(E e) / offerFirst(E e)
取队尾元素并删除 E removeLast() / E pollLast()
取队尾元素但不删除 E getLast() / E peekLast()

使用Stack

  • 栈 LISO
  • 把元素压栈:push(E)
  • 把栈顶的元素“弹出”:pop(E)
  • 取栈顶元素但不弹出:peek(E)
  • Java集合类没有单独的Stack接口,只能用Deque接口来模拟一个Stack
  • 对整数进行进制的转换就可以利用栈

使用Iterator 迭代器

  • 编译器把for each循环通过Iterator改写为普通的for循环
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
     String s = it.next();
     System.out.println(s);
}
  • 使用迭代器的好处在于,调用方总是以统一的方式遍历各种集合类型,而不必关系它们内部的存储结构
  • 集合类实现Iterable接口,该接口要求返回一个Iterator对象;
  • 用Iterator对象迭代集合内部数据。
原文地址:https://www.cnblogs.com/chenshaowei/p/13186078.html