本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。
简述
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象操作,就进行了对对象存储,集合是存储对象最常用的一种方式。而数组和集合类同是容器,有什么不同呢?
数组虽然可以存储对象,但长度是固定的,集合长度可变,并且数组可以存储基本数据类型和对象,而集合只能存储对象。
我们可以看一下Java当中的集合对象
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java01");
arrayList.add("java02");
arrayList.add("java03");
arrayList.add("java04");
System.out.println(arrayList.size());
System.out.println(arrayList);
}
打印结果:
我们如何像操作数据一样操作集合呢?Java中为我们提供了迭代器,其实就是取出集合元素的方式。
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java01");
arrayList.add("java02");
arrayList.add("java03");
arrayList.add("java04");
// 写法一
/*
Iterator iterable = arrayList.iterator();
while (iterable.hasNext()) {
System.out.println(iterable.next());
}
*/
// 写法二,推荐使用这种方式,因为这种方式节省内存,定义在局部变量在执行完就释放了,而while没有。
for (Iterator iterator1 = arrayList.iterator();iterator1.hasNext();){
System.out.println(iterator1.next());
}
}
List
List:元素是有序的,元素可以重复,因为该集合体系有索引(凡是可以操作角标的方法都是该体系特有的方法)。
Set:元素是无序的,元素不可以重复。
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java01");
arrayList.add("java02");
arrayList.add("java03");
arrayList.add("java04");
for(Iterator iterator = arrayList.iterator();iterator.hasNext();) {
Object object = iterator.next();
if (object.equals("java02")) {
arrayList.add("java05");
}
}
}
此时我们发现上面的代码发生了异常,因为在迭代时,通过集合对象操作集合中的元素时,会发生ConcurrentModificationException(并发修改异常),因为你修改了集合元素,但迭代器却不知道。
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java01");
arrayList.add("java02");
arrayList.add("java03");
arrayList.add("java04");
for(Iterator iterator = arrayList.iterator();iterator.hasNext();) {
Object object = iterator.next();
if ("java02".equals(object)) {
iterator.remove();
}
System.out.println(object);
}
System.out.println(arrayList);
}
此时会发现,结果依旧打印出了java02,而在集合中却被移除了,iterator.remover();将java02的引用从集合中删除了,但object 还存在这个引用。
List集合特有的迭代器,ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象操作集合中的元素,因为会发生ConcurrentModificationException(并发修改异常)。所以在迭代器时,只能用迭代器的方式操作元素,因为Iterator方法是有限的,如果想要其他操作,如添加、修改等,就需要使用其子接口,ListIterator该接口只能通过List集合的ListIterator方法获取。
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("java01");
arrayList.add("java02");
arrayList.add("java03");
arrayList.add("java04");
for(ListIterator iterator = arrayList.listIterator(); iterator.hasNext();) {
Object object = iterator.next();
if ("java04".equals(object)) {
iterator.add("java05");
}
System.out.println(object);
}
System.out.println(arrayList);
}
ArrayList:底层的数据结构使用的数组结构。
特点:查询速度很快,增删速度稍慢。(JDK1.2)线程不同步。
Vector
Vector:底层的数据结构使用的数组结构。(JDK1.0)线程同步,被ArrayList替代,不论查询还是增删,都很慢。
枚举是Vector特有的取值方式,发现枚举和迭代器很像,其实枚举和迭代是一样的,因为枚举的名称以及方法的名称过长,所以被迭代器给取代了。
public static void main(String[] args) {
Vector vector = new Vector();
vector.add("java01");
vector.add("java02");
vector.add("java03");
vector.add("java04");
for(Enumeration element = vector.elements(); element.hasMoreElements();) {
System.out.println(element.nextElement());
}
}
LinkedList
LinkedList:底层用的是链表。特点:增删特点很快,查询速度很慢。
LinkedList特有的方法:
- addFirst();
- addLast();
- getFirst();
- getLast(); 获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
- removeFirst();
- removeLast(); 获取元素,并删除元素。如果集合中没有元素,会出现NoSuchElementException
- 在JDK1.5版本中,出现了替代方法。
- offerFirst();
- offerLast();
- peekFirst();
- peekLast();
- pollFirst();
- pollLast(); 如果没有元素,会返回null。
Set
Set:集合是无序的(存入和取出的顺序不一致),元素是不可以重复的。
Set:集合的功能和Collection的是一致的。
常见子类:
HashSet:底层数据结构的哈希表,线程是非同步的。
TreeSet:底层数据结构是二叉树,可以对Set集合中的元素进行排序。
HashSet是如何保证元素的唯一性的呢?
是通过两个方法,hashCode和equals来完成的,如果元素的hashCode值相同,才回判断equals是否为true。如果元素的hashCode值不同,则不会调用equals。
注意,对于判断元素是否存在(contains),以及删除(remove)等操作,依赖的方法都是元素的hashCode和equals方法。
public static void main(String[] args) {
Set set = new HashSet();
set.add(111);
set.add(2222);
set.add(33333);
set.add(444444);
for(Iterator iterator = set.iterator();iterator.hasNext();) {
System.out.println(iterator.next());
}
}
public class SetDemo
{
public static void main(String[] args)
{
HashSet h = new HashSet();
h.add(new Person("zhangsan", 21));
h.add(new Person("lisi", 21));
h.add(new Person("zhangsan", 21));
h.add(new Person("wangwu", 21));
h.add(new Person("zhangsan", 21));
for(Iterator it = h.iterator();it.hasNext();)
{
Person p = (Person)it.next();
System.out.println(p.getAge() + " " + p.getName());
}
}
}
class Person
{
private String name;
private int age;
Person(){
}
Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()
{
return name.hashCode() + age * 37;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
return p.getName().equals(this.getName()) && p.getAge() == this.getAge();
}
}
TreeSet:底层数据结构是二叉树,可以对Set集合中的元素进行排序。
保证元素唯一性的依据是compareTo方法。
TreeSet排序的第一方式:让元素自身具有比较性。元素需要实现Comparable接口,重写compareTo方法,这种方式也称为元素的自然排序,或者叫做默认排序。注意:排序时,当主要条件相同时,一定要判定次要条件。
TreeSet练习
往TreeSet集合中存储自定义对象学生,按照学生的年龄进行排序。
public class SetDemo
{
public static void main(String[] args)
{
TreeSet treeSet = new TreeSet();
treeSet.add(new Student("zhangsan", 19));
treeSet.add(new Student("zhangsan", 50));
treeSet.add(new Student("lisi", 22));
treeSet.add(new Student("lisi", 40));
for(Iterator iterator = treeSet.iterator();iterator.hasNext();) {
System.out.println(iterator.next());
}
}
}
class Student implements Comparable {
private String name;
private int age;
public Student() {
}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if (!(o instanceof Student)) {
throw new RuntimeException("不是学生对象!");
}
Student student = (Student)o;
if (this.age > (student.getAge())) {
return 1;
} if (this.age == student.age) {
return this.name.compareTo(student.name);
}
return -1;
}
}
TreeSet排序的第二种方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的,这时就需要让集合具备比较性。在集合初始化就有了比较方式。这时就需要定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
注意:当两种排序都在存在时,以外部比较器为主。
public class SetDemo
{
public static void main(String[] args)
{
TreeSet treeSet = new TreeSet(new MyCompare());
treeSet.add(new Student("zhangsan", 19));
treeSet.add(new Student("zhangsan", 50));
treeSet.add(new Student("lisi", 22));
treeSet.add(new Student("lisi", 40));
for(Iterator iterator = treeSet.iterator();iterator.hasNext();) {
System.out.println(iterator.next());
}
}
}
class Student implements Comparable{
private String name;
private int age;
public Student() {
}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if (!(o instanceof Student)) {
throw new RuntimeException("不是学生对象!");
}
Student student = (Student)o;
if (this.age > (student.getAge())) {
return 1;
} if (this.age == student.age) {
return this.name.compareTo(student.name);
}
return -1;
}
}
class MyCompare implements Comparator {
@Override
public int compare(Object o1, Object o2) {
Student student1 = (Student)o1;
Student student2 = (Student)o2;
int num = student1.getAge() - student2.getAge();
if (num == 0) {
return student1.getName().compareTo(student2.getName());
}
return num;
}
}
Map集合
我们接下来说一说集合中的另一个接口Map。Map集合存储着键值对,一对一对往里存,并且还要保证键的唯一性。该集合不用迭代器进行取出元素。
Map集合的常见子类:
- HashTable:底层使用的是哈希表数据结构,不可以存入null键或null值(当key或者value为null时,运行时会抛出NullPointerException),线程同步(jdk1.0)。
- HashMap:底层使用的是哈希表数据结构,允许存入null值或null值,线程不同步(jdk1.2)。
- TreeMap:底层使用的二叉树,线程不同步。可以用于对Map集合中的键进行排序。
我们可以发现,该集合和Set集合很像,其实Set底层就是使用了Map集合。
Map集合常用的方法:
put(K key, V value);如果添加时,出现相同的键,那么后添加的值会覆盖原有键对应值。并返回覆盖那个值。
clear();删除集合里所有的元素。
remove();根据key值删除某一元素。
boolean containsKey(Object value);
boolean containskey(Object key);
boolean isEmpty();
get(Object key):可以通过get方法返回值来判断一个键是否已经存在,通过返回的null判断。
value();用来获取Map集合中所有的值。
Map集合的两种取出方法:
entrySet()
:将Map集合中的映射关系存入到set集合中,而这个关系的数据类型就是:Map.Entry.
keySet()
:将Map中所有的键都存入到Set集合,因为Set具备迭代器,所有可以用迭代的方式取出所有的键,如果使用get方法,获取的是每一个键对应的值。
keySet()用例
public static void main(String[] args) {
HashMap map = new HashMap<String, String>();
map.put("1", "张三");
map.put("2", "李四");
map.put("3", "王五");
map.put("4", "刘六");
// 先获取map集合中所有键的集合
Set<String> keySet = map.keySet();
// 有了Set集合,就可以获取其迭代器
for(Iterator<String> iterator = keySet.iterator();iterator.hasNext();) {
String key = iterator.next();
System.out.println("key:" + key + "value:" + map.get(key));
}
System.out.println("---------------------------------");
for(String key : keySet) {
System.out.println("key:" + key + "value:" + map.get(key));
}
}
打印结果:
我们可以通过下图来简单了解一下keySet取值方式
entrySet用例
public static void c(String[] args) {
HashMap map = new HashMap<String, String>();
map.put("1", "张三");
map.put("2", "李四");
map.put("3", "王五");
map.put("4", "刘六");
// 将Map集合中的映射关系取出,存入Set集合中。
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for(Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();iterator.hasNext();) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + entry.getValue());
}
}
我们可以通过下图来简单了解一下entry取值的方式
HashMap练习
每一个学生都有对应的归属地
学生Student,地址String
学生属性:姓名,年龄
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性。1、描述学生 2、定义Map容器,将学生作为键,地址作为值,存入。 3、获取Map集合中的元素。
public class Demo1 {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap();
map.put(new Student("阿波罗", 6782), "大地之神");
map.put(new Student("阿瑞斯", 2000), "火星");
map.put(new Student("阿迪斯", 899), "冥王星");
map.put(new Student("阿波罗", 6782), "太阳星");
// 使用keySet方式取出
Set<Student> set = map.keySet();
for(Student key : set) {
System.out.println(key + ":" + map.get(key));
}
System.out.println("-----------------------------------");
// 使用entrySet方式取出
Set<Map.Entry<Student, String>> entries = map.entrySet();
for(Iterator<Map.Entry<Student, String>> iterator = entries.iterator();iterator.hasNext();) {
Map.Entry entry = iterator.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
class Student {
private String name;
private int age;
public Student() {
}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
TreeMap练习
获取字符串中每个字母出现的次数
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入字符串");
String str = scanner.nextLine();
char[] chars = str.toCharArray();
TreeMap<Character, Integer> treeMap = new TreeMap<>();
Set<Character> set = treeMap.keySet();
for(char ch : chars) {
if (set.contains(ch)) {
int count = treeMap.get(ch);
treeMap.put(ch, ++count);
} else {
treeMap.put(ch, 1);
}
}
for(char ch : set) {
System.out.print(ch + "(" + treeMap.get(ch) + ")");
}
}
Collections工具类
我们来看看Collections和Collection的区别:
Collections是不属于java的集合框架,它是集合类的一个工具类/帮助类。此类不能被实例化,此类是服务于Collection框架。
Collection是基本的集合接口,继承它的接口有List、Set。
例如:当我们要创建一个集合且该集合中的元素并不保证唯一性,我们会选择List集合,但又对该集合中的元素进行排序,却发现List集合不能对元素排序。(TreeSet集合可以对元素进行排序,但不能存在重复数据)。于是java提供了集合框架的工具类。
该类中的方法都是静态。
sort(List<T> list):根据元素的自然顺序 对指定列表按升序进行排序。
addAll(Collection<? super T> c, T... elements):向目标集合添加若干元素。
binarySearch(List<? extends Comparable<? super T>> list, T key) 使用二分搜索法搜索指定列表,以获得指定对象的下标。
public class TreeMapDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
Collections.addAll(list, "aa", "z", "ggg", "qq", "ssss", "lllll", "ssss", "ggg", "asss");
// 如果自己不定义比较器,会默认按照字母升序
Collections.sort(list, new MyCompare());
// 使用二分法找到该元素的下标,注意,使用该方法查找的集合,必须是有序集合,即得先用sort方法排序
System.out.println(Collections.binarySearch(list, "ggg"));
System.out.println(list);
}
}
class MyCompare implements Comparator<String> {
@Override
public int compare(String str1, String str2) {
if(str1.length() == str2.length()) {
return 0;
}
return str1.length() > str2.length() ? 1:-1;
}
}
4
[z, aa, qq, ggg, ggg, ssss, ssss, asss, lllll]
fill(List<? super T> list, T obj) 使用指定元素替换指定列表中的所有元素。
replaceAll(List<T> list, T oldVal, T newVal) 使用另一个值替换列表中出现的所有某一指定值。
reverse(List<?> list) 反转指定列表中元素的顺序。
reverseOrder() 返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。