JAVA集合

collection集合

集合是存储数据的框架,只能用来存放对象元素,collection集合是最顶层的父类接口,其有2个子接口List和Set,分别代表着两类不同的集合,List接口实现的集合是有序的集合,有索引,元素可以重复;而Set接口实现的集合没有索引,并且不允许存放相同的元素,关于集合的体系结构如下图所示:

- 因为collection集合是接口不能直接使用,所以一般使用多态,将collection集合的对象转变为集合实现类的类型,如下:
colletion <String> coll = new ArrayList<>();
  • collection集合定义了一些常用的公共的方法如下:
    1
    2
    3
    4
    5
    6
    7
    boolean add(E e);   //向集合添加数据
    boolean remove(E e); //删除集合中的元素
    void clear(); //清空集合
    boolean contains(E e); //判断是否含有某个元素
    boolean isEmpty(); //判断集合是否为空
    int size(); //获取集合的长度
    Object[] toArray(); //将集合转换成数组

迭代器Iterator

因为不同集合存储的数据类型可能不一样,所以对集合的存取元素的操作方法也不一样,为了方便统一,我们可以使用Iterator迭代器,它有着对集合的通用操作方法

  • 因为Iterator是一个接口,我们不能直接使用它,所以我们一般通过多态,先建立一个集合对象,然后通过集合对象来实现Iterator接口,建立一个对集合对象的迭代器:

    Collection coll = new LinkedList<>();
    Iterator it = coll.iterator();

  • Iterator也有泛型,但是它必须和调用它的对象的泛型一致,所以Iterator的泛型可以省略不写,但是写上的话必须和集合对象保持一致

  • Iterator的常用方法
    1.boolean hasNext();
    这个方法用于判断集合中是否还有元素,有的话就返回true,使用方法如下:

    Collection coll = new LinkedList<>();
    coll.add(“a”);
    coll.add(“b”);
    Iterator it = coll.iterator();
    boolean b = it.hasNext(); //b=true

2.E next();
这个方法用于取出集合的第一个元素,使用方法如下:

Collection<String> coll = new LinkedList<>();
coll.add("a");
coll.add("b");
Iterator it = coll.iterator();
String s = it.next();   //s="a"

这两个方法都是重复的,所以我们一般是搭配循环来使用:

while(it.hasNext())
{
    System.out.println(it.next());
}
  • iterator迭代器的原理:其实iterator就像一个指针,当创建集合的迭代器时,iterator指针会指向集合索引的-1位置,hasNext就是判断指针下一位是否存在元素,next方法就是将指针移向下一位,并获取指针指向的元素。所以iterator的操作并不会对集合本身产生影响

增强for循环

增强for循环时专门用来遍历数组和集合的,其使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//遍历数组
int a[] = {1, 2, 3};
for (int i : a)
{
System.out.println(i);
}

//遍历集合
Collection<String> coll = new LinkedList<>();
coll.add("a");
coll.add("b");
for (String s : coll) {
System.out.println(s);
}

泛型

当我们不知道使用什么数据类型时,就可以使用泛型

  • 含有泛型的类
    定义含有泛型的类,可以方便我们不知道类的成员的类型的情况,其结构如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class GenericClass<E> {
    E name;
    int age;

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

    public E getName() {
    return name;
    }

    public static void main(String[] args) {
    GenericClass <String> gc = new GenericClass<>();
    gc.setName("Tonm");
    System.out.println(gc.getName());
    }

    }

    在创建含有泛型的类的对象时,如果没有对泛型进行定义,那么默认为Object类型
  • 含有泛型的方法
    在含有泛型的方法时,调用它时传递的参数是什么类型,泛型就时什么类型,如下所示:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class GenericClass<E> {
    E name;
    int age;

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

    public E getName() {
    return name;
    }

    public <M> void typeOfM(M m) {
    System.out.println(m);
    }


    public static void main(String[] args) {
    GenericClass <String> gc = new GenericClass<>();
    gc.typeOfM(1); //M为Interger类型
    gc.typeOfM("string"); //M为String类型
    }

    }
  • 含有泛型的接口
    含有泛型的接口定义有两种方式:

1.在实现接口的类时定义接口的泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//含有泛型的接口定义
public interface GenericInterface <I> {
public void method(I i);
}

//实现接口
public class GenericInterfaceImpl implements GenericInterface<String>{ //泛型定义为String
@Override
public void method(String s){
System.out.println(s);
}

public static void main(String[] args) {
GenericInterfaceImpl gi = new GenericInterfaceImpl();
gi.method("string");
}
}

2.在创建泛型实现类的对象时定义泛型

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericInterfaceImpl<I> implements GenericInterface<I>{
@Override
public void method(I i){
System.out.println(i);
}

public static void main(String[] args) {
GenericInterfaceImpl <Integer> gi = new GenericInterfaceImpl();
gi.method(1);
}
}

  • 泛型通配符
    当使用一个方法时的参数类型不确定时,可以使用泛型通配符?,可以接受任意类型的泛型,如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import java.util.ArrayList;

    public class GenericClass {

    public void printArray(ArrayList<?> array){
    System.out.println(array);
    }

    public static void main(String[] args) {
    GenericClass <String> gc = new GenericClass<>();
    ArrayList <String> array1 = new ArrayList<>();
    array1.add("str1");
    array1.add("arr2");
    ArrayList <Integer> array2 = new ArrayList<>();
    array2.add(1);
    array2.add(2);
    gc.printArray(array1);
    gc.printArray(array2);
    }

    }
    使用类型通配符的好处呢就是,当定义一个方法时需要使用多次,并且每次使用时的类型都不一样,这时候就可以使用类型通配符

LinkedList集合

linkedList集合不同于ArrayList集合之处就在于ArrayList集合底层用数组实现,而LinkedList底层是用链表实现的,相比于ArrayList增删快,查找慢。LinkedList不建议使用多态进行创建,因为使用多态子类的特殊方法就无法使用,LinkedList的一些常用的特有方法如下:

  • addFirst(E e); addFirst方法就是在List的最前面插入一个元素,其作用等同于List的push方法
  • addLast(E e); addLast方法就是在List的最后面插入一个元素,其作用等同于add方法
  • getFirst(); getFirst方法就是获取List的最前面的一个元素
  • getLast(); getLst方法就是获取List最后面的一个元素
  • removeFirst(); removeFirst方法就是删除List最前面的一个元素,等同于pop方法
  • removeLast(); removeLst方法就是删除List最后面的一个元素

HashSet集合

HashSet集合实现了Set接口,其底层使用哈希表实现,优点是访问速度非常快,它有着和set集合一样的特点如下:

- 创建hashSet集合的方法如同List集合,使用多态或者直接创建都可以 - HashSet集合的存储是无序的,并且不会储存相同的元素,如下所示:
1
2
3
4
5
6
Set <String> set = new HashSet<>();
set.add("a");
set.add("c");
set.add("b");
set.add("a");
System.out.println(set); //set=[a,b,c]
- set集合没有索引,所以遍历的时候需要使用iterator迭代器或者增强for循环:
1
2
3
4
5
6
7
8
9
10
11
//iterator迭代器遍历
Iterator <String> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}

//增强for循环遍历
for (String s : set) {
System.out.println(s);
}

## 哈希值 哈希值是1个10进制的整数,是由操作系统给出来的对象的地址值,之歌地址值是逻辑地址值,而不是实际存放的物理地址。 - 在Object类种,有获取对象哈希值的hashCode方法:private int native hashCode();native修饰词代表的是从操作系统中调用的方法。 - Object的toString方法返回的最后一部分就是哈希值的16进制形式
1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 许多类都重写了hashCode方法,如String类,当创建了2个字符串,其内容相等,则他们的哈希值也相同,Integer类也重写了hashCode方法,它的对象返回值就是它的整数大小
1
2
3
4
5
6
7
8
9
10
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode()); //96354
System.out.println(s2.hashCode()); //96354

Integer s3 = new Integer(1);
Integer s4 = new Integer(-333);
System.out.println(s3.hashCode()); //1
System.out.println(s4.hashCode()); // -333

## 哈希表 - 哈希表 = 数组+链表 (老版本) - 哈希表 = 数组+红黑树 (新版本) //红黑树是哈希表访问速度快的重要原因 哈希表的工作原理就是先用数组存放哈希值,然后数组对应的哈希值之下存放集合数据,如下图所示:

Set集合不能存放相同数据的原因

在Set集合中,调用add方法的同时会调用hashCode方法和equals方法,首先会对将要插入集合的对象调用hashcode方法获取其哈希值,然后调用equals方法将其与集合中的元素依次进行比较,如果都不相同,则将该元素放入集合中,相同则不进行放入操作

Collections工具类

Collections工具类是对集合操作的一个工具类,其中具有很多操作集合的方法

  • Collections.addAll(list,element…);
    addAll方法可以一次性向集合中添加多个元素:
    1
    2
    3
    ArrayList <String> arraylist = new ArrayList<>();
    Collections.addAll(arraylist,"a","b","c");
    System.out.println(arraylist); //[a,b,c]
  • Collections.shuffle(list);
    shuffle方法可以将集合中的元素打乱:
    1
    2
    3
    4
    5
    ArrayList <String> arraylist = new ArrayList<>();
    Collections.addAll(arraylist,"a","b","c");
    System.out.println(arraylist); //[a,b,c]
    Collections.shuffle(arraylist);
    System.out.println(arraylist); //[b,c,a]
  • Collections.sort(list);
    sort方法可以对集合中的元素进行排序,注意sort方法只可以对实现了comparable接口的类的集合使用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ArrayList<Integer> list1 = new ArrayList<>();
    Collections.addAll(list1,1,3,2,4);
    System.out.println(list1); //[1,3,2,4]
    Collections.sort(list1);
    System.out.println(list1); //[1,2,3,4]

    ArrayList<String> list2 = new ArrayList<>();
    Collections.addAll(list2,"a","c","d","b");
    System.out.println(list2); //[a,c,d,b]
    Collections.sort(list2);
    System.out.println(list2); //[a,b,c,d]

Map集合

  • map集合在java.util.Map包下,用于保存具有映射关系的数据,是一个双列集合,一个元素包括两个值:key和value.

1.map集合中的元素key和value的类型可以相同,也可以不同
2.map集合中的元素key不可以重复,但是value可以重复
3.map集合中的元素key和value一一对应

  • HashMap底层以哈希表实现,查询速度快,但是存储的元素无序
  • LinkedHashMap底层以哈希表和链表实现,是有序的集合

Map中的一些方法

  • put(k,v);
    put方法用于向集合中添加元素,输出将会以{k1=v1,k2=v2}的形式输出,同时put方法也有返回值,会返回key的value:
    1
    2
    3
    4
    5
    6
    7
    8
    Map <String,String> map = new HashMap<>();
    map.put("p1","p2");
    String v1 = map.put("q1","q2");
    System.out.println(v1); //null
    System.out.println(map); //{q1=q2, p1=p2}
    String v2 = map.put("p1","p3");
    System.out.println(v2); //q2
    System.out.println(map); //{q1=q2, p1=p3}
  • remove(k);
    remove方法可以删除map集合中key为k的元素,并返回对应的value,如果没有则返回null
    1
    2
    3
    String s = map.remove("p1");
    System.out.println(s); //p3
    System.out.println(map); //{q1=q2}
  • get(k);
    get方法用于查找map集合中key为k的value值并返回,没有则返回null
    1
    2
    String v = map.get("q1");
    System.out.println(v); //q3
  • containKey(k);
    containKey用于查找map集合中是否含有key为k的元素:
    1
    2
    3
    4
    Boolean b1 = map.containsKey("q1");
    Boolean b2 = map.containsKey("p1");
    System.out.println(b1); //true
    System.out.println(b2); //false

    Map的遍历

  • 使用KeySet方法将Map的key放置到set集合中,再通过getKey方法获取value
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Map <String,String> map1 = new HashMap<>();
    map1.put("k1","v1");
    map1.put("k2","v2");
    map1.put("k3","v3");
    Set <String> set = map1.keySet();
    Iterator<String> it = set.iterator();
    while(it.hasNext()){
    String key = it.next();
    System.out.println(map1.get(key));
    }
    //v1,v2,v3

    使用map获取字符串的字符出现次数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import java.util.Scanner;
    import java.util.HashMap;

    public class numOfCharInString {
    public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    String str = sc.next();
    System.out.println(str);

    HashMap <Character,Integer> map = new HashMap<Character, Integer>();
    for(char ch : str.toCharArray()){
    if(map.containsKey(ch)){
    Integer value = map.get(ch);
    value++;
    map.put(ch,value);
    }else{
    map.put(ch,1);
    }
    }
    for(Character c : map.keySet()){
    System.out.println(c+":"+map.get(c));
    }
    }
    }