-
Notifications
You must be signed in to change notification settings - Fork 14
Java05. ДЗ 03, Кравцун Андрей, подгруппа 2 #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 03-serializable-string-set
Are you sure you want to change the base?
Changes from all commits
b2dcdca
2b27e5b
7f0348b
534db9c
fa30f95
c5a9455
88f9a68
cf9cd57
4e56c61
f430207
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,224 @@ | ||
| package ru.spbau.mit; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.io.OutputStream; | ||
|
|
||
| public class StringSetImpl implements StringSet, StreamSerializable { | ||
| private static final int BYTES_IN_INT = 4; | ||
| private static final int BITS_IN_BYTE = 8; | ||
| private Vertex root; | ||
|
|
||
| public StringSetImpl() { | ||
| root = null; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. этот конструктор можно убрать:
но можно и оставить, это по желанию |
||
|
|
||
| public boolean add(String element) { | ||
| Vertex current = traverseWord(element, true); | ||
| if (current.isTerminal) { | ||
| return false; | ||
| } else { | ||
| current.isTerminal = true; | ||
| while (current != null) { | ||
| current.subTreeSize++; | ||
| current = current.parent; | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| public boolean contains(String element) { | ||
| Vertex current = traverseWord(element, false); | ||
| return current != null && current.isTerminal; | ||
| } | ||
|
|
||
| public boolean remove(String element) { | ||
| Vertex current = traverseWord(element, false); | ||
|
|
||
| if (current == null || !current.isTerminal) { | ||
| return false; | ||
| } | ||
|
|
||
| current.isTerminal = false; | ||
| for (int i = element.length() - 1; i >= 0; --i) { | ||
| removeOnEmpty(current, element.charAt(i)); | ||
| current = current.parent; | ||
| } | ||
|
|
||
| root.subTreeSize--; | ||
| if (root.subTreeSize == 0) { | ||
| root = null; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| public int size() { | ||
| return root == null ? 0 : root.subTreeSize; | ||
| } | ||
|
|
||
| public int howManyStartsWithPrefix(String prefix) { | ||
| Vertex current = traverseWord(prefix, false); | ||
| return current == null ? 0 : current.subTreeSize; | ||
| } | ||
|
|
||
| public void serialize(OutputStream out) throws SerializationException { | ||
| Vertex.serializeVertex(root, out); | ||
| } | ||
|
|
||
| public void deserialize(InputStream in) throws SerializationException { | ||
| root = Vertex.deserializeVertex(in, null); | ||
| } | ||
|
|
||
| private Vertex traverseWord(String element, boolean addIfNotExists) { | ||
| if (root == null) { | ||
| if (addIfNotExists) { | ||
| root = new Vertex(null); // root only. | ||
| } else { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| Vertex current = root; | ||
|
|
||
| for (int i = 0; i < element.length(); ++i) { | ||
| char c = element.charAt(i); | ||
| int stepCharIndex = Vertex.stepCharIndex(c); | ||
| if (current.next[stepCharIndex] == null) { | ||
| if (addIfNotExists) { | ||
| current.next[stepCharIndex] = new Vertex(current); | ||
| } else { | ||
| return null; | ||
| } | ||
| } | ||
| current = current.next[stepCharIndex]; | ||
| } | ||
| return current; | ||
| } | ||
|
|
||
| private void removeOnEmpty(Vertex current, char stepChar) { | ||
| current.subTreeSize--; | ||
| if (current.subTreeSize == 0 && current.parent != null) { | ||
| int stepCharIndex = Vertex.stepCharIndex(stepChar); | ||
| current.parent.next[stepCharIndex] = null; | ||
| } | ||
| } | ||
|
|
||
| private static void booleanSerialize(boolean b, OutputStream out) throws SerializationException { | ||
| try { | ||
| if (b) { | ||
| out.write(1); | ||
| } else { | ||
| out.write(0); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. можно тернарный оператор использовать |
||
| } catch (IOException e) { | ||
| throw new SerializationException(); | ||
| } | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. про примитивы:
|
||
| private static void intSerialize(int num, OutputStream out) throws SerializationException { | ||
| final int byteMask = 0xFF; | ||
| try { | ||
| for (int i = 0; i < BYTES_IN_INT; ++i) { | ||
| out.write(num & byteMask); | ||
| num >>= BITS_IN_BYTE; | ||
| } | ||
| } catch (IOException e) { | ||
| throw new SerializationException(); | ||
| } | ||
| } | ||
|
|
||
| private static boolean booleanDeserialize(InputStream in) throws SerializationException { | ||
| try { | ||
| int i = in.read(); | ||
| if (i != 1 && i != 0) { | ||
| throw new SerializationException(); | ||
| } | ||
| return i == 1; | ||
| } catch (IOException e) { | ||
| throw new SerializationException(); | ||
| } | ||
| } | ||
|
|
||
| private static int intDeserialize(InputStream in) throws SerializationException { | ||
| try { | ||
| int num = 0; | ||
| for (int i = 0; i < BYTES_IN_INT; ++i) { | ||
| final int readNum = in.read(); | ||
| num |= readNum << (i * BITS_IN_BYTE); | ||
| } | ||
| return num; | ||
| } catch (IOException e) { | ||
| throw new SerializationException(); | ||
| } | ||
| } | ||
|
|
||
| private static class Vertex implements StreamSerializable { | ||
| private static final int CHAR_POWER = 2 * 26; | ||
| private static final int VERTEX_MAGIC = 0xAABBCCDD; | ||
| private static final int EMPTY_VERTEX_MAGIC = 0xDDCCBBAA; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. появление слова magic вносит неразбериху, и другому разработчику может показаться, что автор не очень понимает то, что сам написал)) в данном случае лучше использовать EMPTY_VERTEX_FLAG, NON_EMPTY_VERTEX_FLAG. Значения не очень важны, хоть 0 и 1. |
||
| private final Vertex[] next; | ||
| private boolean isTerminal; | ||
| private Vertex parent; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. final |
||
| private int subTreeSize; | ||
|
|
||
| Vertex(Vertex parent) { | ||
| isTerminal = false; | ||
| next = new Vertex[CHAR_POWER]; | ||
| this.parent = parent; | ||
| subTreeSize = 0; | ||
| } | ||
|
|
||
| public static void serializeVertex(Vertex v, OutputStream out) throws SerializationException { | ||
| if (v == null) { | ||
| intSerialize(EMPTY_VERTEX_MAGIC, out); | ||
| } else { | ||
| v.serialize(out); | ||
| } | ||
| } | ||
|
|
||
| // Question: constructor would be better? | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. отсылаю к практике, когда обсуждались конструкторы и статик методы. мне текущий вариант больше нравится, он симметричен к методу выше |
||
| public static Vertex deserializeVertex(InputStream in, Vertex parent) throws SerializationException { | ||
| final int magic = intDeserialize(in); | ||
| if (magic == EMPTY_VERTEX_MAGIC) { | ||
| return null; | ||
| } else if (magic == VERTEX_MAGIC) { | ||
| Vertex v = new Vertex(parent); | ||
| v.deserialize(in); | ||
| return v; | ||
| } else { | ||
| throw new SerializationException(); | ||
| } | ||
| } | ||
|
|
||
| public static int stepCharIndex(char stepChar) { | ||
| if (Character.isLowerCase(stepChar)) { | ||
| return (int) stepChar - 'a'; | ||
| } else { | ||
| return (CHAR_POWER / 2) + (int) (stepChar - 'A'); | ||
| } | ||
| } | ||
|
|
||
| public void serialize(OutputStream out) throws SerializationException { | ||
| intSerialize(VERTEX_MAGIC, out); | ||
| for (int i = 0; i < next.length; ++i) { | ||
| if (next[i] == null) { | ||
| intSerialize(EMPTY_VERTEX_MAGIC, out); | ||
| } else { | ||
| next[i].serialize(out); | ||
| } | ||
| } | ||
| booleanSerialize(isTerminal, out); | ||
| intSerialize(subTreeSize, out); | ||
| } | ||
|
|
||
| public void deserialize(InputStream in) throws SerializationException { | ||
| for (int i = 0; i < next.length; ++i) { | ||
| next[i] = deserializeVertex(in, this); | ||
| } | ||
|
|
||
| isTerminal = booleanDeserialize(in); | ||
| subTreeSize = intDeserialize(in); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package ru.spbau.mit; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. тесты сильно усложнять не всегда имеет смысл, лучше написать побольше простых случаев, включая какие-то крайние |
||
|
|
||
| import org.junit.Test; | ||
|
|
||
| import java.io.*; | ||
|
|
||
| import static org.junit.Assert.*; | ||
|
|
||
| public class StreamSerializableTest { | ||
| @Test(expected = SerializationException.class) | ||
| public void testFailEmpty() { | ||
| testDeserialization(new ByteArrayOutputStream()); | ||
| } | ||
|
|
||
| @Test(expected = SerializationException.class) | ||
| public void testFailDummyHeader() { | ||
| final int dummyHeader = 0xBABEFAFA; | ||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
| writeInteger(dummyHeader, out); | ||
| testDeserialization(out); | ||
| } | ||
|
|
||
| @Test | ||
| public void testEmpty() { | ||
| StringSetImpl s = new StringSetImpl(); | ||
| assertEmptyStringSetImpl(s); // just for sureness. | ||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
| s.serialize(out); | ||
| StringSetImpl s1 = testDeserialization(out); | ||
| assertEmptyStringSetImpl(s1); | ||
|
|
||
| String[] arr = {"abc", "cde", ""}; | ||
| for (String str : arr) { | ||
| assertTrue(s1.add(str)); | ||
| } | ||
|
|
||
| for (String str : arr) { | ||
| assertTrue(s1.remove(str)); | ||
| } | ||
|
|
||
| out.reset(); | ||
| s1.serialize(out); | ||
|
|
||
| StringSetImpl s2 = testDeserialization(out); | ||
| assertEmptyStringSetImpl(s2); | ||
| } | ||
|
|
||
| private static void writeInteger(int num, OutputStream out) { | ||
| final int bytesInInteger = 4; | ||
| final int bitsInByte = 8; | ||
| try { | ||
| for (int i = 0; i < bytesInInteger; ++i) { | ||
| final int lowestByte = num & ((1 << bitsInByte) - 1); | ||
| num >>= bitsInByte; | ||
| out.write(lowestByte); | ||
| } | ||
| } catch (IOException e) { | ||
| fail(); | ||
| } | ||
| } | ||
|
|
||
| private static StringSetImpl testDeserialization(ByteArrayOutputStream out) throws SerializationException { | ||
| StringSetImpl stringSet = new StringSetImpl(); | ||
| ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); | ||
| ((StreamSerializable) stringSet).deserialize(in); | ||
| return stringSet; | ||
| } | ||
|
|
||
| private static void assertEmptyStringSetImpl(StringSetImpl s) { | ||
| assertNotNull(s); | ||
| assertEquals(0, s.size()); | ||
| assertEquals(0, s.howManyStartsWithPrefix("")); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
особого смысла в отдельном исключении нет, по желанию -- можно делать своё, можно IO