Capítulo 2 Notas de Origem da Coleção-Java

1 Análise de código-fonte ArrayList e idéias de design

1.1 Estrutura geral

ArrayList é uma estrutura de matriz.

  • Permitir colocar valor nulo, ele será expandido automaticamente;
  • A complexidade de tempo de métodos como tamanho, isEmpty, get, set e add são todos O (1);
  • Não é thread-safe.No caso de multi-threading, é recomendável usar a classe thread-safe: Collections # synchronizedList;
  • Aprimore o loop for ou use um iterador para iterar, se o tamanho da matriz for alterado, ele falhará rapidamente e lançará uma exceção.

1.2 Inicialização

Três métodos: inicialização direta sem parâmetros, inicialização de tamanho especificado, inicialização de dados iniciais especificados.

// 无参数直接初始化
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 // 指定大小初始化
 public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
 public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
  • Inicialização direta sem parâmetros, o tamanho padrão é uma matriz vazia, não 10. 10 é o valor da primeira expansão.

1.3 Implementação nova e ampliada

  • 1,5 vezes a capacidade original após a expansão;
  • A expansão máxima é Integer.MAX_VALUE;
  • Ao adicionar, não há uma verificação estrita do valor, portanto, ArrayList permite valores nulos.
  • Para expandir a essência , crie uma nova matriz e copie a matriz antiga. Consome mais desempenho; portanto, especifique o tamanho dos dados o máximo possível para evitar a expansão frequente.

1.4 Excluir

Exclua um elemento indexado por índice, os elementos atrás avançarão um pouco.

1.5 Iterador

int cursor;// 迭代过程中,下一个元素的位置,默认从 0 开始。
int lastRet = -1; // 新增场景:表示上一次迭代过程中,索引的位置;删除场景:为 -1。
int expectedModCount = modCount;// expectedModCount 表示迭代过程中,期望的版本号;modCount 表示数组实际的版本号。
public boolean hasNext() {
            return cursor != size;
        }
 public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

2 Análise de código-fonte LinkedList

2.1 Estrutura geral

Insira a descrição da imagem aqui
Código do nó

	private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

2.2 Adicionar, excluir

Pode ser adicionado do início ao fim, alguns podem ser excluídos do início

2.3 Consulta do nó

Se o índice estiver no primeiro semestre, inicie a pesquisa desde o início, caso contrário, inicie a pesquisa pela parte de trás, o que melhora a eficiência. size >> 1 significa deslocar para a direita 1 bit, o que equivale a 2.

Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

2.4 Iterador

Implemente a interface ListIterator e ofereça suporte à iteração bidirecional, ou seja, é possível iterar do início ou do fim.

3 Lista de perguntas da entrevista

3.1 Fale sobre o entendimento de ArrayList e LinkedList

Você pode responder primeiro à arquitetura geral e, em seguida, analisar alguns detalhes. Por exemplo, a parte inferior do ArrayList é uma matriz, e sua API é para encapsular a matriz inferior e assim por diante.

3.2 Problemas de expansão

1. ArrayList é construído sem o construtor de parâmetros.Agora, adicione um valor.No momento, qual é o tamanho da matriz e qual é o tamanho máximo disponível antes da próxima expansão?
No momento, o tamanho da matriz é 1 e o tamanho máximo disponível antes da próxima expansão é 10. O valor padrão da primeira expansão é 10.
2. Se adicionar novos valores à lista continuamente, quando eu aumentar para a 11ª, o tamanho da matriz será Quanto?
oldCapacity + (oldCapacity >> 1), a próxima expansão é 1,5 vezes. Isso é de 10 a 15.
3. Depois que a matriz é inicializada e um valor é adicionado, se eu usar o método addAll e adicionar 15 valores de uma vez, qual é o tamanho da matriz final?
Após a inicialização, adicione um valor. No momento, a expansão da capacidade padrão é 10 e, em seguida, adicione 15 valores de uma vez. A expansão da capacidade é de 1,5 vezes, ou seja, 15, ainda não pode ser satisfeita. Existe uma estratégia no momento:

	private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity; // 直接扩容到我们需要的大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

Expansão direta para 16.
4. Agora eu tenho uma matriz grande para copiar, o tamanho original da matriz é 5k, como posso copiar rapidamente?
5k é um valor relativamente grande e a capacidade especificada é 5k quando a matriz é inicializada. Se a inicialização não especificar um tamanho, ela será freqüentemente expandida, com um grande número de cópias, afetando o desempenho.
5. Por que a expansão da capacidade consome desempenho?
A camada inferior usa System.arraycopy para copiar a matriz original para a nova matriz, portanto, consome desempenho.
6. Há algo que valha a pena aprender com o processo de expansão do código-fonte?

  • Expansão automática, os usuários não precisam se preocupar com alterações na estrutura de dados subjacente. A expansão da capacidade é baseada em um crescimento de 1,5 vezes. O crescimento no estágio inicial é relativamente lento e o crescimento no estágio posterior é relativamente grande. O tamanho dos dados usados ​​na maioria dos trabalhos não é grande. Existe utilização para melhorar o desempenho.
  • Durante o processo de expansão, preste atenção ao estouro da matriz, que não é menor que 0 e não é maior que o valor máximo de Inteiro.

3.3 Problemas de exclusão

1. Existe um ArrayList, os dados são 2, 3, 3, 3, 4 e existem três 3s no meio.Agora eu uso para (int i = 0; i <list.size (); i ++), desejo alterar o valor para 3 Elementos excluídos, posso excluí-los corretamente? Qual é o resultado da exclusão final e por quê? O código de exclusão é o seguinte:

List<String> list = new ArrayList<String>() {{
            add("2");
            add("3");
            add("3");
            add("3");
            add("4");
        }};
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("3")) {
                list.remove(i);
            }
        }

Não pode. Insira a descrição da imagem aqui
2. Ainda a matriz ArrayList acima, podemos excluir aprimorando o loop for?
Não, ele reportará um erro. Como o processo aprimorado para loop realmente chama o método next () do iterador. Quando você chama o método list # remove () para excluir, o valor de modCount será +1, mas o valor de esperadoModCount no iterador não foi alterado. , Causando a próxima vez que o iterador executar o método next (), o esperadoModCount! = ModCount relatará o erro ConcurrentModificationException.
Veja: Por que o Alibaba proíbe a operação de remoção / adição de elementos no loop foreach
3. A matriz acima, se pode ser excluída usando o método Iterator.remove () ao excluir, por que?
Sim, porque o método Iterator.remove () atribuirá o modCount mais recente ao esperadoModCount durante a execução, para que durante o próximo ciclo, tanto o modCount quanto o esperadoModCount sejam iguais.
4. As três perguntas acima são iguais para o LinkedList?
Sim, embora a estrutura subjacente do LinkedList seja uma lista duplamente vinculada, mas para os três problemas acima, os resultados são consistentes com o ArrayList.

3.4 Problemas de contraste

1. Qual é a diferença entre ArrayList e LinkedList?
A lógica subjacente dos dois é diferente: ArrayList é uma matriz e LinkedList é uma lista duplamente vinculada. As APIs implementadas na parte inferior também são diferentes, bla, bla, etc.
2. Quais são os diferentes cenários de aplicativos de ArrayList e LinkedList?
ArrayList é adequado para o cenário de pesquisa rápida sem adição e exclusão frequentes, enquanto o LinkedList é adequado para o cenário de adição e exclusão freqüente e poucas consultas.
3. ArrayList e LinkedList têm capacidade máxima? O
primeiro tem o maior número inteiro, o último é teoricamente sem fio, mas o tamanho real é int size, portanto, existem apenas números inteiros.
4. Como ArrayList e LinkedList lidam com valores nulos?
Ambos executam a adição e exclusão de valores nulos, mas ArrayList exclui valores nulos desde o início.
5. ArrayList e LinedList são seguros para threads, por que?
Não. Em um ambiente multithread, operações como adição e exclusão não são sincronizadas.
6. Como resolver o problema de segurança da rosca?
Existem listas seguras de encadeamento em Collections, Collections.synchronizedList () ou CopyOnWriteArrayList.

3.5 Outros tipos de tópicos

1. Descreva a lista duplamente vinculada?
Ligeiramente.
2. Descreva a adição e exclusão de listas duplamente vinculadas
.

4 Análise do código fonte do HashMap

Publicado 97 artigos originais · elogiados 3 · 10.000+ visualizações

Acho que você gosta

Origin blog.csdn.net/qq_39530821/article/details/105347974
Recomendado
Clasificación