Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
/.idea/
/JavaCourse_HW_1_9.iml
/out/
35 changes: 35 additions & 0 deletions src/GarriksCoolerLinkedList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
public class GarriksCoolerLinkedList<K, E> extends GarriksLinkedList<K, E> {

public E getByKey(Object key) {
for (GarriksNode<K, E> node : this) {
if (node.getKey().equals(key)) {
return node.getItem();
}
}
return null;
}

public K getKey(int index) {
return getNode(index).getKey();
}

public void addMap(K key, E element) {
for (GarriksNode<K, E> node : this) {
if (node.getKey().equals(key)) {
node.setKey(key);
return;
}
}
add(element, key);
}

public boolean add(E o, K key) {
GarriksNode<K, E> prevNode = tail.getPrev();
GarriksNode<K, E> newNode = new GarriksNode<>(o, key, tail, prevNode);
prevNode.setNext(newNode);
tail.setPrev(newNode);
len++;
return true;
}

}
159 changes: 159 additions & 0 deletions src/GarriksHashMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class GarriksHashMap<K, V> implements HashMapInterface<K, V> {
private final int MINIMAL_LEN_CAUSING_REPLACEMENT = 13;
Copy link
Owner

@iaulitin iaulitin Dec 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Эти константы могут быть статическими.
Обычно именно static final константы пишут в UPPER_SNAKE_CASE

private final int DEFAULT_CAPACITY = 16;
private final double DEFAULT_LOAD_FACTOR = 0.75;
private GarriksCoolerLinkedList<K, V>[] array;
private int capacity;
private double loadFactor;
private int len;

public GarriksHashMap() {
createNewHashMap();
}

@Override
public int size() {
return len;
}

@Override
public boolean isEmpty() {
return len == 0;
}

@Override
public boolean containsKey(Object key) {
int bucket = getBucketNum(key);
if (array[bucket] != null) {
for (GarriksNode<K, V> node : array[bucket]) {
if (node.getKey().equals(key)) {
return true;
}
}
}
return false;
}


@Override
public boolean containsValue(Object value) {
for (GarriksLinkedList<K, V> bucket : array) {
if (bucket != null) {
for (GarriksNode<K, V> node : bucket) {
if (node.getItem().equals(value)) {
return true;
}
}
}
}
return false;
}

@Override
public Collection<V> values() {
List<V> result = new ArrayList<>();
for (GarriksLinkedList<K, V> bucket : array) {
if (bucket != null) {
for (GarriksNode<K, V> node : bucket) {
result.add(node.getItem());
}
}
}
return result;
}

@Override
public V put(K key, V value) {
checkSize();
int bucket = getBucketNum(key);
createBucket(bucket);
array[bucket].addMap(key, value);
len++;
return value;
}

@Override
public V get(Object key) {
int bucket = getBucketNum(key);
return array[bucket].getByKey(key);
}

@Override
public V remove(Object key) {
checkSize();
int bucket = getBucketNum(key);
V result = array[bucket].getByKey(key);
array[bucket].remove(result);
if (array[bucket].size() == 0) {
array[bucket] = null;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А для чего потом зануляешь опустевший массив? Если для экономии памяти - не уверен, что стоит этим заниматься - там памяти мизер, а ты теперь вынужден всегда беспокоиться - есть у тебя список в бакете или нет. Если не допускать ситуацию наличия пустого бакета, жить будет лучше, жить будет веселей.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я это делал намеренно. Мне показалось, что так удобнее и быстрее проверять, есть ли элементы в корзине. Если корзина пустая, то соответсвующей ячейке присваевается null.
Все таки лучше оставлять в корзинах head и tail?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я бы оставлял.

Во-первых, фактор сложности кода. Меньше действий - проще код.
Во-вторых, производительность. Да, памяти будет потреблять чуть больше, зато каждая вставка в "пустой" бакет не будет приводить к созданию нового списка и тд. Особенно будет больно, если интенсивно вставлять элементы в мапу и удалять.

len--;
return result;
}

@Override
public void clear() {
createNewHashMap();
}

private int getBucketNum(Object key) {
return Math.abs(key.hashCode() % capacity);
}

private void checkSize() {
if (len > loadFactor * capacity) {
capacity *= 2;
replacementArray();
}
if (len > MINIMAL_LEN_CAUSING_REPLACEMENT && capacity > len * loadFactor / 2) {
capacity /= 2;
replacementArray();
}
}

private void replacementArray() {
GarriksCoolerLinkedList<K, V>[] newArray = new GarriksCoolerLinkedList[capacity];
for (GarriksCoolerLinkedList<K, V> bucket : array) {
if (bucket == null) {
continue;
}
for (GarriksNode<K, V> node : bucket) {
if (newArray[getBucketNum(node.getKey())] == null) {
newArray[getBucketNum(node.getKey())] = new GarriksCoolerLinkedList<>();
}
int bucketIndex = getBucketNum(node.getKey());
K currKey = node.getKey();
V currItem = node.getItem();
newArray[bucketIndex].addMap(currKey, currItem);
}
}
array = newArray;
}

private void createBucket(int index) {
if (array[index] == null) {
array[index] = new GarriksCoolerLinkedList<>();
}
}

private void createNewHashMap() {
array = new GarriksCoolerLinkedList[16];
capacity = DEFAULT_CAPACITY;
loadFactor = DEFAULT_LOAD_FACTOR;
len = 0;
}

@Override
public String toString() {
StringBuilder result = new StringBuilder();
for (GarriksLinkedList<K, V> bucket : array) {
result.append(bucket).append("\n");
}
return result.toString();
}

}
168 changes: 168 additions & 0 deletions src/GarriksLinkedList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import java.util.Iterator;

public class GarriksLinkedList<T, E> implements LinkedListInterface<E>, Iterable<GarriksNode<T, E>> {


protected final GarriksNode<T, E> head;
protected final GarriksNode<T, E> tail;
protected int len;

public GarriksLinkedList() {
this.head = new GarriksNode<>(null, null, null);
this.tail = new GarriksNode<>(null, null, head);
this.head.setNext(this.tail);
this.len = 0;
}

public GarriksNode<T, E> getHead() {
return head;
}

@Override
public int size() {
return len;
}

@Override
public boolean isEmpty() {
return len == 0;
}

@Override
public boolean contains(Object o) {

if (isEmpty()) {
return false;
}

GarriksNode<T, E> currNode = head.getNext();

if (currNode.getItem().getClass() != o.getClass()) {
return false;
}

while (currNode.getItem() != null && !currNode.getItem().equals(o)) {
currNode = currNode.getNext();
}
return currNode.getItem() != null && currNode.getItem().equals(o);
}

@Override
public boolean add(E o) {
GarriksNode<T, E> prevNode = tail.getPrev();
GarriksNode<T, E> newNode = new GarriksNode<>(o, tail, prevNode);
prevNode.setNext(newNode);
tail.setPrev(newNode);
len++;
return true;
}

@Override
public void add(int index, E element) {
GarriksNode<T, E> currNode = index == 0 ? head : getNode(index - 1);
GarriksNode<T, E> nextNode = currNode.getNext();
GarriksNode<T, E> newNode = new GarriksNode<>(element, nextNode, currNode);
currNode.setNext(newNode);
nextNode.setPrev(newNode);
len++;
}


@Override
public E get(int index) {
return getNode(index).getItem();
}


@Override
public int indexOf(Object o) {
GarriksNode<T, E> currNode = head;
for (int i = 0; i < len; i++) {
currNode = currNode.getNext();
if (o.equals(currNode.getItem())) {
return i;
}
}
return -1;
}

@Override
public E set(int index, E element) {
getNode(index).setItem(element);
return element;
}

@Override
public E remove(int index) {
GarriksNode<T, E> currItem = getNode(index);
GarriksNode<T, E> prevItem = currItem.getPrev();
GarriksNode<T, E> nextItem = currItem.getNext();
prevItem.setNext(nextItem);
nextItem.setPrev(prevItem);
len--;
return currItem.getItem();
}

@Override
public boolean remove(Object o) {
GarriksNode<T, E> currItem = getNode(o);
if (currItem.equals(tail) || currItem.equals(head)) {
return false;
}
GarriksNode<T, E> prevItem = currItem.getPrev();
GarriksNode<T, E> nextItem = currItem.getNext();
prevItem.setNext(nextItem);
nextItem.setPrev(prevItem);
len--;
return true;
}

@Override
public void clear() {
head.setNext(tail);
tail.setPrev(head);
len = 0;
}

public Iterator<GarriksNode<T, E>> iterator() {
return new GarriksLinkedListIterator<>(this);
}

protected GarriksNode<T, E> getNode(int index) {
if (index >= len || index < 0) {
throw new IndexOutOfBoundsException(String.format("Attempt to get the index %d, in range 0..%d", index, len));
}
GarriksNode<T, E> currNode = head;
for (int i = 0; i < index + 1; i++) {
currNode = currNode.getNext();
}
return currNode;
}

protected GarriksNode<T, E> getNode(Object o) {
GarriksNode<T, E> currNode = head;
for (int i = 0; i < len; i++) {
if (currNode.getItem().equals(o)) {
return currNode;
}
currNode = currNode.getNext();
}
return currNode;
}



@Override
public String toString() {
GarriksNode<T, E> currElem = head;
StringBuilder result = new StringBuilder("head <-> ");
for (int i = 0; i < len; i++) {
currElem = currElem.getNext();
result.append(currElem.getItem()).append(" <-> ");
}
result.append("tail");

return result.toString();
}

}
25 changes: 25 additions & 0 deletions src/GarriksLinkedListIterator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import java.util.Iterator;
import java.util.NoSuchElementException;

public class GarriksLinkedListIterator<T, E> implements Iterator<GarriksNode<T, E>> {

private GarriksNode<T, E> currElem;

public GarriksLinkedListIterator(GarriksLinkedList<T, E> arr) {
this.currElem = arr.getHead();
}

@Override
public boolean hasNext() {
return currElem.getNext().getItem() != null;
}

@Override
public GarriksNode<T, E> next() {
if (!this.hasNext()) {
throw new NoSuchElementException("Garriks iterator can't see more elements :)");
}
currElem = currElem.getNext();
return currElem;
}
}
Loading