Java进阶第七章——数据结构:Collection

数据结构:Collection

本章所涉及到的数据结构知识可在数据结构学习记录中学习:
咖啡ice的数据结构学习记录

1.Collection常用方法

  • 没有使用“泛型”之前,Collection中可以存储Object中的所有子类型。

  • 集合不能直接存储基本数据类型,也不能存储java对象。只能存储java对象的内存地址。

  • Collection中常用方法:

    往集合里加元素:boolean add(Object e)

    获取集合中元素个数:int size()

    清空集合:void clear()

    判断集合是否包含元素:boolean contains(Object o)

    删除集合中某个元素:boolean remove(Object o)

    判断集合是否为空:boolean isEmpty()

    把集合转换成数组:Object[] toArray()

package collection;import java.util.ArrayList;
import java.util.Collection;public class CollectionTest {public static void main(String[] args) {//创建一个集合对象//Collection c = new Collection();  接口是抽象的,无法实例化Collection c = new ArrayList<>();//用一个父类型指向子类型。c.add(1200);//自动装箱,实际上放进去了一个对象的内存地址Integer x = new Integer(1200);c.add(new Object());System.out.println("集合中元素个数:"+ c.size());  //集合中元素个数:2//清空集合c.clear();System.out.println("集合中元素个数:"+ c.size());  //集合中元素个数:0c.add("ice");c.add("coffee");c.add("is");c.add("perfect");boolean flag = c.contains("coffee");System.out.println(flag); //trueboolean flag2 = c.contains("coffee2");System.out.println(flag2); //falseSystem.out.println("集合中元素个数:"+ c.size());  //集合中元素个数:4c.remove("is");System.out.println("集合中元素个数:"+ c.size());  //集合中元素个数:3System.out.println(c.isEmpty());  //falsec.clear();System.out.println(c.isEmpty());  //truec.add("abc");c.add("def");c.add(100);c.add("ice-coffee");Object[] objs = c.toArray();for (int i = 0; i < objs.length; i++) {System.out.printf(objs[i] + " ");  //abc def 100 ice-coffee}}
}
  • 所有Collection通用的一种方法:迭代方式(也就是遍历)。

  • 在Map集合中不能用迭代器,而在Collection以及子类中使用。

  • 迭代器定义的变量类似于指针,且不是指向第一个元素。,可以使用两个方法来迭代对象Iterator中方法:

    ​ boolean hasNext() 如果仍有元素可以迭代(遍历),则返回true。如果false表示没有更多元素可以迭代。

    ​ Object next() 返回迭代的下一个元素。

    注:也就是说迭代器对象指向要输出位置的前一个位置

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest01 {public static void main(String[] args) {Collection c = new ArrayList();//存储时的类型和取出的类型一样c.add("ice");c.add("coffee");c.add(100);Iterator it = c.iterator();while(it.hasNext()){ //下一个元素存在,返回trueSystem.out.printf(it.next() + "  ");  //ice  coffee  100}}
}
  • HashSet集合:无序不可重复
public class CollectionTest01 {public static void main(String[] args) {Collection c = new HashSet();//存进去和取出来顺序不一样c.add(100);+c.add(200);c.add(300);c.add(100);c.add(40);Iterator it = c.iterator();while(it.hasNext()){System.out.printf(it.next() + "  ");  //100  200  40  300 }}
}
  • contains()方法:

    boolean contains(Object o):判断集合中是否包含某个对象o,如果包含返回true,如果不包含返回false。

    注:在底层中,contains()方法调用equals()方法,所以放在集合里边的对象需要重写equals()方法。

public class CollectionTest01 {public static void main(String[] args) {Collection c = new ArrayList();c.add(new String("abc"));c.add(new String("def"));String x = new String("abc"); //由于String对象在new时会放到字符串常量池,创建相同的字段时会指向同一个位置。System.out.println(c.contains(x));  //trueUser u1 = new User("coffee");User u2 = new User("coffee");c.add(u1);  System.out.println(c.contains(u2));//这里没有重写,这个结果比较的是内存地址,结果是:false}
}
class User{private String name;public User(){}public User(String name){this.name = name;}
}

contains方法在底层会调用equals方法进行比较,所以这里对象重写了equals方法则会是true

public class CollectionTest {public static void main(String[] args) {Collection c = new ArrayList();User u1 = new User("coffee");User u2 = new User("coffee");c.add(u1);System.out.println(c.contains(u2)); //true}
}
class User{private String name;public User(){  }public User(String name){ this.name = name; }//重写equals方法@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return Objects.equals(name, user.name);}
}
  • remove()方法:

    boolean remove(Object o):删除集合中包含某个对象o,如果包含返回true,如果不包含返回false。

    注:在底层中,remove()方法调用equals()方法,所以放在集合里边的对象需要重写equals()方法。

public class CollectionTest01 {public static void main(String[] args) {Collection c = new ArrayList();String s1 = new String("hello");String s2 = new String("hello");c.add(s1);c.remove(s2);System.out.println(c.size());  //0}
}
  • 综上所属,remove和contains两个方法在底层都需要调用equals方法,所以放在集合里边的对象需要重写equals()方法。

2.List常用方法

  • List集合特点:每个元素有下标(下标从0开始),可以有重复元素。相当于数据结构中的线性表(数组)。

  • List作为Collection的子接口,list接口有自己常用的特色方法:

    在指定位置插入指定元素:void add(int index,E element)

    返回指定下标元素:E get(int index)

    返回元素第一次出现索引:int indexOf(Object o)

    返回元素最后一次出现索引:int lastIndexOf(Object o)

    移除指定下标元素:E remove(int index)

    用指定元素替换指定位置元素:E set(int index,E element)

package list;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class ListTest{public static void main(String[] args) {//创建list类型集合List mylist = new ArrayList();//add()默认在数组末尾添加元素mylist.add("ice");mylist.add("coffee");mylist.add("is");mylist.add("nice");Iterator it = mylist.iterator();while(it.hasNext()){Object elt = it.next();System.out.printf(elt + " ");  //ice coffee is nice}System.out.println();  //分割线//add(index,element)在指定位置插入添加指定元素//这个方法使用效率低,使用情况较少mylist.add(1,"milk");Iterator it2 = mylist.iterator();while(it2.hasNext()){Object elt = it2.next();System.out.printf(elt + " ");  //ice milk coffee is nice}System.out.println();//分割线//根据下标获取元素get()方法System.out.println(mylist.get(0));  //ice//由于List有下标,List有独特的遍历方式for循环for (int i = 0; i < mylist.size(); i++) {System.out.printf(mylist.get(i) + " ");  //ice milk coffee is nice}System.out.println();//分割线//获取指定对象第一次出现处索引System.out.println(mylist.indexOf("is")); //3mylist.add(3,"coffee");//获取指定对象最后一次出现索引System.out.println(mylist.lastIndexOf("coffee")); //3//删除指定下标位置的元素mylist.remove(2);mylist.remove(1);for (int i = 0; i < mylist.size(); i++) {System.out.printf(mylist.get(i) + " ");  //ice coffee is nice}System.out.println();//分割线//修改指定位置元素mylist.set(3,"good");for (int i = 0; i < mylist.size(); i++) {System.out.printf(mylist.get(i) + " ");  //ice coffee is good}}
}
  • 注:计算机英语常用增删改查几个单词

    增:add、save、new

    删:delete、drop

    改:update、set、modify

    查:find、get、query、select

3.List实现类——ArrayList

  • ArrayList特性:

    ①ArrayList集合初始化容量是10。

    ②ArrayList集合底层是Object类型的数组Object[]。

    ③构造方法:new ArrayList(); 或者 List list2 = new ArrayList(20);

    ④一般给定一个预估价初始化容量,减少数组扩容次数。每次扩容会扩1.5倍。

    ⑤数组检索效率高,使用频率最多。

    ⑥随机增删元素效率比较低。但是在末尾增加效率高不受影响。

    ⑦ArrayList是非线程安全的

package list;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;public class ArrayListTest {public static void main(String[] args) {//默认初始化容量10List list1 = new ArrayList();//size()方法是获取当前集合中元素个数,而不是获取集合容量。System.out.println(list1.size()); //0//指定初始化容量List list2 = new ArrayList(20);//通过构造方法将HashSet集合转换成List集合Collection c = new HashSet();c.add("ice");c.add("coffee");List list3 = new ArrayList(c);for (int i = 0; i < list3.size(); i++) {System.out.printf(list3.get(i) + " ");  //coffee ice }}
}
  • 将非线程安全的ArrayList变成线程安全方法:Collections.synchronizedList
public class ArrayListTest {public static void main(String[] args) {List mylist = new ArrayList();Collections.synchronizedList(mylist);mylist.add("111");}
}

4.List实现类——LinkedList

  • LinkedList底层为双向链表数据结构。LinkedList集合底层也是有下标的,不过底层是双向链表,所以查找效率较低。

  • 与ArrayList对比:

    LinkedList在检索(查找)效率较低,在增删方面效率较高。

    ArrayList在检索(查找)效率较高,在增删方面效率较低。不过只在末尾添加元素时效率还是很高的。

    所以ArrayList比较常用。

import java.util.LinkedList;
import java.util.List;
public class LinkedListTest01 {public static void main(String[] args) {List list = new LinkedList();list.add("ice");list.add("coffee");for (int i = 0; i < list.size(); i++) {System.out.printf(list.get(i) + " ");}}
}

5.List实现类——Vector

  • Vector底层也是一个数组,初始化容量为10。
  • 扩容:扩容之后是原容量的2倍。
  • Vector中所有方法都是线程同步的,都带有synchronized关键字,是线程安全的,效率比较低,使用较少。

import java.util.Iterator;
import java.util.Vector;public class VectorTest {public static void main(String[] args) {Vector vector = new Vector();vector.add(1);vector.add(2);vector.add(3);vector.add(4);vector.add(5);vector.add(6);vector.add(7);vector.add(8);vector.add(9);vector.add(10);Iterator it = vector.iterator();while(it.hasNext()){Object obj = it.next();System.out.printf(obj + " ");  //1 2 3 4 5 6 7 8 9 10 }}
}

6.机制补充—泛型

  • JDK5.0后推出的新特性:泛型

  • 在没有使用泛型时,it.next()取出来的是Object类型,使用起来不方便。

public class GenericTest {public static void main(String[] args) {List myList = new ArrayList();Cat c = new Cat();Bird b = new Bird();myList.add(c);myList.add(b);Iterator it = myList.iterator();while(it.hasNext()){// Animal a = it.next();迭代器取出的是Object,所以这么写会报错Object obj = it.next();if(obj instanceof Animal){Animal a = (Animal)obj;a.move();}}}
}
class Animal{public void move(){System.out.println("动物在移动");}
}
class Cat extends Animal{public void catchMouse(){System.out.println("猫抓老鼠");}
}
class Bird extends Animal{public void fly(){System.out.println("鸟在飞");}
}
  • 在JDK5之后泛型机制,表示List中只允许存储某个类型(例如Animal)的数据,语法:List<Animal>;
  • 使用泛型,集合中元素类型能够统一。
public class GenericTest {public static void main(String[] args) {List<Animal> myList = new ArrayList<Animal>();Cat c = new Cat();Bird b = new Bird();myList.add(c);myList.add(b);//myList.add("abc");只能存储Animal类型的数据,不允许存储String或其他类型//这个表示迭代器迭代的是Animal类型Iterator<Animal> it = myList.iterator();while(it.hasNext()){//使用泛型后,每一次迭代返回的数据都是Animal类型Animal a =it.next();//不需要强制类型转换,直接调用a.move();}}
}
class Animal{public void move(){System.out.println("动物在移动");}
}
class Cat extends Animal{public void catchMouse(){System.out.println("猫抓老鼠");}
}
class Bird extends Animal{public void fly(){System.out.println("鸟在飞");}
}
  • JDK8后引入,自动类型推断机制(即钻石表达式):List<Animal> myList = new ArrayList<>();
public class GenericTest {public static void main(String[] args) {List<Animal> myList = new ArrayList<>();myList.add(new Animal());myList.add(new Cat());myList.add(new Bird());//myList.add("abc");只能存储Animal类型的数据,不允许存储String或其他类型Iterator<Animal> it = myList.iterator();while(it.hasNext()){Animal a = it.next();a.move();}}
}
  • 可以自定义泛型,自定义泛型时<>里边是一个标识符,可以随便写。一般写<T>或者写<E>
public class GenericTest<E> {public void doSome(E o){System.out.println(o);}public static void main(String[] args) {GenericTest<String> gt = new GenericTest<>();gt.doSome("icecoffee");//gt.doSome(123); 报错,类型不匹配GenericTest<Integer> gt2 = new GenericTest<>();//gt2.doSome("icecoffee");报错,类型不匹配gt2.doSome(123);}
}

7.增强for循环foreach

  • 增强for(foreach)语法:变量名是用来标记数组中的元素。

    for(元素类型 变量名 : 数组名){ }

public class ForEachTest {public static void main(String[] args) {int[] arr = {123,11,4,1453,45,21};for(int data : arr){System.out.printf(data + " "); //123 11 4 1453 45 21 }}
}
  • foreach有一个缺点:没有下标,在需要使用下标的循环中,不建议使用foreach
public class ForEachTest {public static void main(String[] args) {List<String> myList = new ArrayList<>();myList.add("ice");myList.add("coffee");myList.add("is");myList.add("nice");for (String s: myList) {System.out.printf(s + " ");  //ice coffee is nice }}
}

8.Set常用方法

  • HashSet集合元素:存入时与取出时不一定一样,可重复,没有下标。
public class SetTest {public static void main(String[] args) {Set<String> strs = new HashSet<>();//HashSet中add实际上是存储到HashMap的key部分。strs.add("ice");strs.add("coffee");strs.add("coffee");strs.add("very");strs.add("good");strs.add("very");strs.add("good");for (String s:strs) {System.out.printf(s + " "); //very coffee ice good }}
}
  • TreeSet集合:存入时与取出时不一定一样,不可重复,没有下标。但是存储的元素自动按照大小排序。
public class SetTest {public static void main(String[] args) {Set<String> strs = new TreeSet<>();strs.add("A");strs.add("C");strs.add("V");strs.add("R");strs.add("N");strs.add("J");strs.add("O");for (String s:strs) {System.out.printf(s + " "); //A C J N O R V}}
}

——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频