Java学习笔记 : 集合

本章对比C++中的STL学习,并多翻文档


集合

集合概述

  • 集合实际上就是一个容器,可以来容纳其它类型的数据
  • 集合不能直接存储基本数据类型或者java对象,集合中存储的是引用
  • java中每个不同的集合底层会对应不同的数据结构
  • java中集合分为两大类,以单个方式存储元素这类集合的超级父接口是Collection,以键值对方式存储元素这类集合超级父接口是Map

所有集合类和集合接口都在java.util.*包下

集合继承结构图

Collection继承结构.png
Map继承结构图.png

Collection

Collection的作用就是规定了一个集合有哪些基本的操作

Collection常用方法

Collection c = new ArrayList();     //Collection为接口,不能直接创建
c.add(100);                         //自动装箱,返回boolean
c.add(new Object);                  
int size = c.size();                //返回集合元素个数
c.isEmpty();                        //判断集合是否为空  boolean
c.contains(100);                    //判断集合中是否包含某元素  boolean
c.remove(100);                      //删除集合中某个元素 返回boolean
c.clear();                          //清空集合
Object[] objs = c.toArray();        //将集合转化为对象数组

contains()深入:

Collection c = new ArrayList();     //Collection为接口,不能直接创建
String s1 = new String("abc");
String s2 = new String("abc");
c.add(s1);
c.contains(s2);                     //true
  • contains()方法底层调用的是equals方法,String类重写了equals方法,所以比较的是内容,而不是内存地址
  • Collection中存储的对象必须重写equals方法

remove()深入:

Collection c = new ArrayList();     //Collection为接口,不能直接创建
String s1 = new String("abc");
String s2 = new String("abc");
c.add(s1);
c.remove(s2);
c.isEmpty();                        //True

remove()方法中使用equals方法比较两个对象并删除

Collection 工具类

Collection c = new ArrayList();     //Collection为接口,不能直接创建
c.add(100);
//....
Collections.sort(c);                    //排序,要求元素类型实现Comparable接口
Collections.sort(list,Comparator);              //排序,调用Comparator

迭代器

  • 以下迭代方式只适用于Collection,在Map中不适用
  • 迭代器初始位置为集合中第一个元素的前面
  • 集合结构只要发生改变,迭代器必须重新获取,否则会出现异常
  • 获取迭代器遍历集合,相当于对当前集合状态拍了一个快照,迭代器参照快照进行迭代
Collection c = new ArrayList();     //Collection为接口,不能直接创建
c.add(1);
c.add(2);
c.add(3);
Iterator it = c.iterator();         //返回集合的一个迭代器对象
while(it.hasNext()){                //判断当前迭代器下一个位置是否有对象
    Object obj = it.next();         //迭代器指向下一个位置并返回对象
                                    //注意没有C++中的it++或it=it->next
    it.remove();                    //删除迭代器指向的当前元素
}

List接口

  • List接口为Collection的子接口
  • List集合存储有序可重复(有下标)

常用方法

List list = new ArrayList();            //创建List类型的集合

//添加元素
list.add(1);
list.add(2);
list.add(3);
list.add(0,4);                          //(index,element)

//迭代
Iterator it = list.iterator();
while(it.hasNext()){
    Object obj = it.next();
}

Object obj = list.get(index);           //根据下标获得元素
int first = list.indexOf(3);            //返回特定元素第一次出现的下标
list.set(index,7);                      //修改指定位置的元素
list.remove(index);                     //删除指定位置的元素

ArrayList

容量与扩容

ArrayList 底层是Object类型的数组,初始化容量为10

List list1 = new Arraylist();       //默认初始化容量10
List list2 = new Arraylist(20);     //初始化容量20
list1.size();                       //0

扩容时容量增长为原来的1.5倍,建议给定一个预估计的初始化容量,减少数组扩容次数

构造方法

List list1 = new Arraylist();               
List list2 = new Arraylist(capacity);
List list3 = new Arraylist(collection);     //可用于集合间的转换

LinkedList

底层实现为双向链表,所以根据顺序表与链表的优缺点选择合适的数据结构

List list = new LinkedList();
list.add("A");
list.add("B");
list.add("C");
for(int i = 0;i

Vector

  • 底层为数组,初始化容量为10
  • 扩容时容量增长为原来的两倍(同C++中的vector)
  • vector中所有方法都是线程同步的,效率比较低,使用较少
//基本代码参考List接口
Collections.synchronizedList(myList);       //可将线程不安全的list转化为线程安全的

泛型机制

泛型使用

  • 用泛型来指定集合中的数据类型,这样之后集合中数据类型可以统一,从集合中取出的类型是规定的类型,不需要进行大量类型转换
  • 泛型是只在编译阶段起作用,在程序运行阶段没用
List list = new ArrayList();        //创建只能存储String类的ArrayList
list,add("abc");
list.add("def");
//list.add(100);            //报错
Iterator it = list.iterator();      //创建指定类型迭代器
while(it.hasNext()){
    String str = it.next();                 //否则只能使用Object接受
    System.out.println(str);
}

自动类型推断机制(钻石表达式)

个人认为就是少打几个字

List list = new ArrayList<>();
list.add("abc");
list.add("def");
Iterator it = list.iterator();      //创建指定类型迭代器
while(it.hasNext()){
    String str = it.next();                 //否则只能使用Object接受
    System.out.println(str);
}

自定义泛型

也就是C++中的类模板

public class MyTemplate {
    public void doSome(dataType data){
        System.out.println(data);
    }
}

public static void main(String[] args){
    MyTemplate mt = new MyTemplate<>();
    mt.doSome("abc");
}

HashSet和TreeSet

HashSet用法可当作C++STL中的unordered_set

TreeSet用法可当作C++STL中的set

自定义类型排序

类需要实现java.lang.Comparable接口才能在set中被自动排序

方法一:实现Comparable接口

//实例,客户先按年龄再按姓名大小存入set:
class Customer implements Comparable{     //实现接口
    private int age;
    private String name;
    
    public int compareTO(Customer c){               //比较函数,根据返回的正负排序
        if(this.age == c.age){
            return this.name.compareTo(c.name);     //String类已经实现compareTo
        }
        else{ 
            return this.age-c.age;
        }
    }
}

方法二:使用比较器

class Customer {        
    private int age;
    private String name;
}

class CustomerComparator implements Comparator{
    public int compare(Customer c1, Customer c2){
        //指定比较规则
        if(this.age == c.age){
            return this.name.compareTo(c.name);
        }
        else{ 
            return this.age-c.age;
        }
    }
}

public class Test{
    public static void main(String[] args){
        Treeset customers = new Treeset<>(new CustomerComparator());
        //创建Treeset时需要传递一个比较器进去
    }
}

方法三:使用匿名内部类

public class Test{
    public static void main(String[] args){
        Treeset customers = new Treeset<>(new CustomerComparator(){
        if(this.age == c.age){
            return this.name.compareTo(c.name);
        }
        else{ 
            return this.age-c.age;
        }
        });
        //new Comparator时写比较规则
    }
}

Map

Map集合以键值对存储数据,key和value都是引用数据类型

Map与Collection没有继承关系

Map常用方法

Map map = new HashMap<>();           //创建map对象
map.put(key1,value1);                           //向map中添加键值对
map.put(key2,value2);
map.size();                                     //获取键值对数量
Collection values =  map.values()        //将所有值提取放入集合
map.remove(key1);                               //删除某个key对应的键值对
Value value = map.get(key2);                    //获取某个关键字对应的值
map,contains(value2);                           //判断是否包含某个value,底层调用equals
map.isEmpty();                                  //判断是否为空

Map集合的遍历

//先提取所有的key,使用迭代器遍历key
Set keys = map.keySet();
Iterator it = keys.iterator();
while(it.hasNext()){
    Key key = it.next();
}

//foreach
Set keys = map.keySet();
for(Key key : keys){
   ... 
}

//使用map集合中entrySet()方法
Set> set = map.entrySet();
for(Map.Entry node : set){
    System.out.println(node.getKey()+"--->"+node.getValue());
}

entrySet()可理解为将键值对转化为一个对象(Map.Entry)


HashMap

  • HashMap底层为散列表,使用拉链法处理冲突
  • HashMap初始容量为16(官方推荐为2的次方,可以使散列更均匀),当装填因子达到0.75时开始扩容
  • Hashmap可以允许key和value为null,而HashTable不允许
  • JDK8中,当拉链中一个链表元素个数超过8时,自动转化为红黑树存储,当红黑树元素个数小于6之后自动恢复单向链表
  • HashMap在比较元素时先调用HashCode方法确定元素位置,随后在单向链表中(大于一个元素)使用equals方法比较元素,所以类中当equals方法可以判断两个类相同时,需要重写HashCode方法使两个类HashCode方法返回结果相同

使用Alt+insert同时重写HashCode和equals方法

Map map = new HashMap<>();
map.put(1,"zhangsan");
map.put(2,"lisi");
map.put(3,"wangwu");
map.put(3,"zhaoliu");       //当key相同时value覆盖

properties

properties是属性类,继承HashTable,它的key和value都是String类

Properties p = new Properties();
p.setProperty("username","zhangsan");           //存属性
p.setproperty("password","123");
String username = p.getProperty("username");        //取属性

你可能感兴趣的:(Java学习笔记 : 集合)