Java基础-集合
ArrayList
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
Collection
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
package array.demo1;
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection<String> s = new ArrayList<String>();
//添加元素
s.add("hello");
s.add("world");
s.add("!");
System.out.println(s);
//移除元素
s.remove("!");
System.out.println(s);
//判断集合中是否存在元素
System.out.println(s.contains("hello"));
//判断集合是否为空
System.out.println(s.isEmpty());
//输出集合的长度
System.out.println(s.size());
}
}
/**
[hello, world, !]
[hello, world]
true
false
2
**/
Iterator
Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。
- next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
- hasNext() 用于检测集合中是否还有元素。
- remove() 将迭代器返回的元素删除。
package array.demo2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class App {
public static void main(String[] args) {
Collection<Student> s = new ArrayList<Student>();
Student s1 = new Student("张三", 8);
Student s2 = new Student("李四", 9);
Student s3 = new Student("王五", 10);
s.add(s1);
s.add(s2);
s.add(s3);
Iterator<Student> it = s.iterator();
while (it.hasNext()){
Student student= it.next();
System.out.println(student.getName() +", "+ student.getAge());
}
}
}
List
有序集合(也称为序列)。该界面的用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。
与集合不同,列表通常允许重复的元素。更正式地,列表通常允许成对的元素e1和e2,使得e1.equals (e2),并且如果它们允许空元素,它们通常允许多个空元素。有人可能希望实现一个禁止重复的列表,当用户尝试插入时会抛出运行时异常,但是我们预计这种使用是罕见的。
package array.demo3;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("!");
System.out.println(list);
//根据位置添加函数
list.add(2, "java");
System.out.println(list);
//根据位置返回元素
String s = list.get(1);
System.out.println(s);
System.out.println(list);
//删除指定位置元素 返回被删除元素
s = list.remove(2);
System.out.println(s);
System.out.println(list);
//修改指定位置元素 返回被修改元素
s = list.set(2, "!!!");
System.out.println(s);
System.out.println(list);
//索引遍历
for (int i = 0; i < list.size(); i++) {
s = list.get(i);
System.out.print(s+' ');
}
System.out.println();
Iterator<String> it = list.iterator();
while (it.hasNext()){
String i = it.next();
System.out.print(i+' ');
}
}
}
/**
[hello, world, !]
[hello, world, java, !]
world
[hello, world, java, !]
java
[hello, world, !]
!
[hello, world, !!!]
hello world !!!
hello world !!!
**/
并发修改异常
package array.demo3;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test2 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("!");
System.out.println(list);
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
if (s.equals("world")){
list.add("java");
}
}
}
}
报错
查看源码
// java/util/ArrayList.java
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
......
/**
判断 modCount 是否等于 expectedModCount 否则返回错误
modCount:实际修改次数
expectedModCount:预期修改次数
**/
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致
可以使用集合遍历来对对象进行操作
Listlterator
列表迭代器,通过List集合的listlterator()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代具有更多元素,则返回trueE
- previous():返回列表中的上一个元素
- boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
- void add(E e):将指定的元素插入列表
package array.demo3;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Test3 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("!");
System.out.println(list);
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String s = it.next();
if (s.equals("hello")){
it.add("java");
}
System.out.println(s);
}
System.out.println(list);
while(it.hasPrevious()){
String s = it.previous();
System.out.println(s);
}
}
}
增强for循环
继承自接口lterator ,也会导致并发修改异常
package array.demo3;
import java.util.ArrayList;
import java.util.List;
public class Test4 {
public static void main(String[] args) {
int[] intArr = {1,2,3,4,5};
for (int i : intArr){
System.out.print(i+" ");
}
System.out.println("\n--------------------");
String[] strArr = {"hello", "world", "!"};
for (String s : strArr){
System.out.print(s+" ");
}
System.out.println("\n--------------------");
List<String> strList = new ArrayList<String>();
strList.add("hello");
strList.add("world");
strList.add("!");
for (String s :strList){
System.out.print(s+" ");
}
}
}
/**
1 2 3 4 5
--------------------
hello world !
--------------------
hello world !
**/
Linklist
ArrayList: 底层数据结构是数组,查询快,增删慢
LinkedList: 底层数据结构是链表,查询慢,增删快
- public void addFirst(Ee) 在该列表开头插入指定的元素
- public void addLast(E e) 将指定的元素追加到此列表的末尾
- publicE getFirst() 返回此列表中的第一个元素
- public E getLast(J) 返回此列表中的最后一个元素
- public E removeFirst() 从此列表中删除并返回第一个元素
- public E removeLast() 从此列表中删除并返回最后一个元素
package array.demo3;
import java.util.LinkedList;
public class Test5 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<String>();
list.add("hello");
list.add("world");
list.add("!");
System.out.println(list);
list.addFirst("Bob");
list.addLast("java");
System.out.println(list);
System.out.println(list.getFirst());
System.out.println(list.getLast());
list.removeFirst();
System.out.println(list);
list.removeLast();
System.out.println(list);
}
}
/**
[hello, world, !]
[Bob, hello, world, !, java]
Bob
java
[hello, world, !, java]
[hello, world, !]
**/
Set
set,集合。不允许出现重复元素、集合中的元素位置无顺序、有且只有一个值为null的元素。
互异性:一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。
无序性:一个集合中,每个元素的地位都是相同的,元素之间是无序的。集合上可以定义序关系,定义了序关系后,元素之间就可以按照序关系排序。但就集合本身的特性而言,元素之间没有必然的序。
空集的性质:空集是一切集合的子集
package array.demo4;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
s.add("hello");
s.add("world");
s.add("!");
s.add("hello");
System.out.println(s);
}
}
/**
[!, world, hello]
**/
哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中的public int hashCode(): 返回对象的哈希码值
同一个对象多次调用hashCode0方法返回的哈希值是相同的
package array.demo4;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
Student s1 = new Student("Bob", 18);
System.out.println(s1.hashCode());
System.out.println(s1.hashCode());
//默认情况下不同对象的哈希值不同
Student s2 = new Student("Bob", 18);
System.out.println(s2.hashCode());
}
}
/**
2129789493
2129789493
668386784
**/
特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
linkedhash
- 哈希表和链表实现的set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素
package array.demo4;
import java.util.LinkedHashSet;
public class Test2 {
public static void main(String[] args) {
LinkedHashSet<String> l = new LinkedHashSet<String>();
l.add("hello");
l.add("world");
l.add("java");
for (String s : l){
System.out.println(s);
}
}
}
/**
hello
world
java
**/
TreeSet
- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然非序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以不包含重复元素的集合
package array.demo4;
import java.util.TreeSet;
public class Test3 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(10);
ts.add(40);
ts.add(20);
ts.add(50);
ts.add(30);
ts.add(50);
for (Integer i : ts){
System.out.println(i);
}
}
}
/**
10
20
30
40
50
**/
CompareTo
compareTo() 方法用于两种方式的比较:字符串与对象进行比较、按字典顺序比较两个字符串。
- 如果参数字符串等于此字符串,则返回值 0;
- 如果此字符串小于字符串参数,则返回一个小于 0 的值;
- 如果此字符串大于字符串参数,则返回一个大于 0 的值。
package array.demo4;
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Student s){
int num = this.age - s.age; //先对年龄进行排序
int num2 = num == 0 ? this.name.compareTo(s.name):num;
return num2;
}
}
package array.demo4;
import java.util.TreeSet;
public class Test4 {
public static void main(String[] args) {
Student s1 = new Student("Bob", 18);
Student s2 = new Student("Jack", 19);
Student s3 = new Student("Alice", 16);
Student s4 = new Student("Tom", 24);
TreeSet<Student> ts = new TreeSet<Student>();
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
for (Student s : ts){
System.out.println(s.getName() + " : " + s.getAge());
}
}
}
/**
Alice : 16
Bob : 18
Jack : 19
Tom : 24
**/
泛型
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
泛型定义格式
- <类型>:指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2.…>︰指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
package array.demo5;
public class Generic <T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
package array.demo5;
public class test {
public static void main(String[] args) {
Generic c1 = new Generic<String>();
c1.setT("Bob");
System.out.println(c1.getT());
Generic c2 = new Generic<Integer>();
c2.setT(26);
System.out.println(c2.getT());
}
}
泛型方法
泛型方法的定义格式:
修饰符<类型>返回值类型方法名(类型变量名){}
范例: public
package array.demo5;
/**
public class Generic2 {
public void show(String s){
System.out.println(s);
}
public void show(Integer i){
System.out.println(i);
}
public void show(Boolean b){
System.out.println(b);
}
}
**/
//泛型改进
/**
public class Generic2<T>{
public <T> void show(T t){
System.out.println(t);
}
}
**/
//泛型方法
public class Generic2 {
public <T> void show(T t){
System.out.println(t);
}
}
类型通配符
-
类型通配符: List: 表示元素类型未知的List,它的元素可以匹配任何的类型
这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中 -
类型通配符上限:<? extends类型>
List<? extends Number>:它表示的类型是Number或者其子类型 -
类型通配符下限:<? super类型>
list<? super Number>:它表示的类型是Number或者其父类型
package array.demo5;
import java.util.List;
import java.util.ArrayList;
public class Test3 {
public static void main(String[] args) {
//<?>
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<Number>();
List<?> list3 = new ArrayList<Integer>();
//<? extends 类型>
// List<? extends Number> list4 = new ArrayList<Object>();
List<? extends Number> list5 = new ArrayList<Number>();
List<? extends Number> list6 = new ArrayList<Integer>();
//<? super 类型>
List<? super Number> list7 = new ArrayList<Object>();
List<? super Number> list8 = new ArrayList<Number>();
// List<? super Number> list9 = new ArrayList<Integer>();
}
}
可变参数
Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项。当可变参数个数多余一个时,必将有一个不是最后一项,所以只支持有一个可变参数。因为参数个数不定,所以当其后边还有相同类型参数时,java无法区分传入的参数属于前一个可变参数还是后边的参数,所以只能让可变参数位于最后一项。
package array.demo5;
public class Test4 {
public static void main(String[] args) {
System.out.println(sum(10, 20));
System.out.println(sum(10, 20, 30));
System.out.println(sum(10, 20, 30, 40));
System.out.println(sum(10, 20, 30, 40, 50));
}
public static int sum(int ...a){
int sum = 0;
for (int i : a ){
sum += i;
}
return sum;
}
}
可变参数应用
- public static
List asList(T... a): 返回由指定数组支持的固定大小的列表 返回的集合不能做增册操作,可以做修改操作 - List接口中有一个静态方法 public static
List of(E... elements):返回包含任意数量元素的不可变列表返回的集合不能做增删改操作 - Set接口中有一个静态方法:
public staticSet of(E... elements):返回一个包含任意数量元素的不可变集合在给元素的时候,不能给重复的元素
返回的集合不能做增删操作,没有修改的方法
package array.demo5;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class Test5 {
public static void main(String[] args) {
List<String> list = Arrays.asList("hello", "world", "java");
//list.add("a");
//list.remove("java");
list.set(2, "javaee");
System.out.println(list);
List<String> list2 = List.of("hello", "world", "java");
//list2.add("a");
//list2.remove("java");
//list2.set(2, "javaee");
Set<String> set = Set.of("hello", "world", "java"); //不能有重复参数
//set.add("a");
//set.remove("java");
//set.set(2, "javaee");
System.out.println(set);
}
}
Map
Map 接口中键和值一一映射. 可以通过键来获取值。
- 给定一个键和一个值,你可以将该值存储在一个 Map 对象。之后,你可以通过键来访问对应的值。
- 当访问的值不存在的时候,方法就会抛出一个 NoSuchElementException 异常。
- 当对象的类型和 Map 里元素类型不兼容的时候,就会抛出一个 ClassCastException 异常。
- 当在不允许使用 Null 对象的 Map 中使用 Null 对象,会抛出一个 NullPointerException 异常。
- 当尝试修改一个只读的 Map 时,会抛出一个 UnsupportedOperationException 异常。
相关方法
- put(K key,V value) 添加元素
- remove(Object key) 根据键删除键值对元素
- void clear() 移除所有的键值对元素
- boolean containsKey(Object key) 判断集合是否包含指定的键
- boolean containsValue(Object value) 判断集合是否包含指定的值
- boolean isEmpty() 判断集合是否为空
- int size() 集合的长度,也就是集合中键值对的个数
package map.demo1;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<String, String> m = new HashMap<String, String>();
m.put("test001", "Bob");
m.put("test002", "Alice");
m.put("test003", "Jack");
m.put("test003", "Test");
System.out.println(m);
System.out.println("-----------------");
System.out.println(m.remove("test001"));
System.out.println(m.remove("test005"));
System.out.println(m);
System.out.println("-----------------");
System.out.println(m.containsKey("test002"));
System.out.println(m.containsKey("test005"));
System.out.println("-----------------");
System.out.println(m.isEmpty());
System.out.println(m.size());
System.out.println("-----------------");
m.clear();
System.out.println(m);
}
}
/**
{test002=Alice, test003=Test, test001=Bob}
-----------------
Bob
null
{test002=Alice, test003=Test}
-----------------
true
false
-----------------
false
2
-----------------
{}
**/
获取功能
- v get(Object key) 根据键获取值
- Set
keySet() 获取所有键的集合 - Collection
values() 获取所有值的集合 - Set<Map.Entry<K, V> entrySet() 获取所有键值对对象的集合
package map.demo1;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test2 {
public static void main(String[] args) {
Map<String, String> m = new HashMap<String, String>();
m.put("test001", "Bob");
m.put("test002", "Alice");
m.put("test003", "Jack");
System.out.println(m);
System.out.println("-----------------");
System.out.println(m.get("test001"));
System.out.println("-----------------");
Set<String> keySet = m.keySet();
for (String key : keySet){
System.out.println(key);
}
System.out.println("-----------------");
Collection<String> values = m.values();
for (String v : values){
System.out.println(v);
}
}
}
遍历
package map.demo1;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test3 {
public static void main(String[] args) {
Map<String, String> m = new HashMap<String, String>();
m.put("test001", "Bob");
m.put("test002", "Alice");
m.put("test003", "Jack");
//keySet()
Set<String> keySet = m.keySet();
for (String key : keySet){
String value = m.get(key);
System.out.println(key + ',' + value);
}
//entrySet()
Set<Map.Entry<String, String>> entrySet = m.entrySet();
for (Map.Entry<String, String> me : entrySet){
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
Collections
collections类是针对集合操作的工具类
Collections类的常用方法
public static <T extends Comparable<? super T>> void sort (List<T> list)
将指定的列表按升序排序public static void reverse (list<?> list)
反转指定列表中元素的顺序public static void shuffle (List<?> list)
使用默认的随机源随机排列指定的列表
package map.demo2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(30);
list.add(10);
list.add(20);
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
Collections.sort(list);
System.out.println(list);
Collections.shuffle(list);
System.out.println(list);
}
}
/**
[30, 10, 20]
[20, 10, 30]
[10, 20, 30]
[20, 10, 30]
**/
Test
需求:通过程序实现斗地主过程中的洗牌,发牌和看牌。要求:对牌进行排序
package map.poker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
public class Poker {
public static void main(String[] args) {
//生成牌
HashMap<Integer, String> map= new HashMap<Integer, String>();
ArrayList<Integer> array = new ArrayList<Integer>();
String[] colors = {"♦","♣","♥","♠"};
String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
int index=0;
for (String number : numbers){
for (String color : colors){
map.put(index, color+number);
array.add(index);
index++;
}
}
map.put(index, "小王");
array.add(index);
index++;
map.put(index, "大王");
array.add(index);
Collections.shuffle(array);
//System.out.println(map);
//发牌
TreeSet<Integer> player01 = new TreeSet<Integer>();
TreeSet<Integer> player02 = new TreeSet<Integer>();
TreeSet<Integer> player03 = new TreeSet<Integer>();
TreeSet<Integer> domain = new TreeSet<Integer>();
for (int i = 0; i < array.size(); i++) {
int x = array.get(i);
if(i >= array.size()-3){
domain.add(x);
} else if(i%3==0){
player01.add(x);
} else if(i%3==1){
player02.add(x);
} else{
player03.add(x);
}
}
//看牌
lookPoker("player001", player01, map);
lookPoker("player002", player02, map);
lookPoker("player003", player03, map);
lookPoker("domain", domain, map);
}
//看牌
public static void lookPoker(String name, TreeSet<Integer> ts ,HashMap<Integer, String> hm){
System.out.println(name+ "的牌:");
for (Integer key : ts){
String poker = hm.get(key);
System.out.print(poker+" ");
}
System.out.println();
}
}
player001的牌:
♥3 ♣4 ♦5 ♥5 ♣6 ♦8 ♣8 ♥8 ♦10 ♠10 ♣J ♥Q ♦K ♥A ♥2 小王 大王
player002的牌:
♦3 ♠5 ♦6 ♥6 ♠6 ♦7 ♥7 ♠7 ♠8 ♦9 ♣9 ♠9 ♥J ♦Q ♥K ♦A ♣2
player003的牌:
♣3 ♠3 ♦4 ♥4 ♠4 ♣5 ♥9 ♣10 ♥10 ♦J ♠J ♠Q ♣K ♣A ♠A ♦2 ♠2
domain的牌:
♣7 ♣Q ♠K