diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf14531 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Project exclude paths +/.gradle/ +/sample/build/ +/sample/build/classes/java/main/ +/treemultiset/build/ +/treemultiset/build/classes/java/main/ +/treemultiset/build/classes/java/test/ \ No newline at end of file diff --git a/.idea/$PRODUCT_WORKSPACE_FILE$ b/.idea/$PRODUCT_WORKSPACE_FILE$ new file mode 100644 index 0000000..3733e0d --- /dev/null +++ b/.idea/$PRODUCT_WORKSPACE_FILE$ @@ -0,0 +1,19 @@ + + + + + + + 1.8 + + + + + + + + \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..cb0592b --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..bc8d0a3 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..62a079e --- /dev/null +++ b/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java' +} + +group 'com.yry' +version '1.0-SNAPSHOT' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..87b738c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..5249857 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Mar 16 13:28:01 CST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..af6708f --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..0f8d593 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..5b23f5b --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} + +group 'com.yry' +version '1.0-SNAPSHOT' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + implementation project(":treemultiset") +} diff --git a/sample/src/main/java/PerformanceTest.java b/sample/src/main/java/PerformanceTest.java new file mode 100644 index 0000000..e9de7a8 --- /dev/null +++ b/sample/src/main/java/PerformanceTest.java @@ -0,0 +1,24 @@ +import java.util.Random; + +/** + * PerformanceTest + * + * @author: yry + * @date: 2020/3/16 + */ +public class PerformanceTest { + + private static final int MAXN = 1000000; + + public static void main(String[] args) { + Random random = new Random(); + TreeMultiSet treeMultiSet = new TreeMultiSet<>(); + for (int i = 0; i < MAXN; i++) { + treeMultiSet.add(i); + } + + System.out.println("size: " + treeMultiSet.size()); + System.out.println("diffElementSize: " + treeMultiSet.diffElementSize()); + } + +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..88d2775 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'TreeMultiSet1' +include 'treemultiset' +include 'sample' + diff --git a/treemultiset/build.gradle b/treemultiset/build.gradle new file mode 100644 index 0000000..62a079e --- /dev/null +++ b/treemultiset/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java' +} + +group 'com.yry' +version '1.0-SNAPSHOT' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/treemultiset/src/main/java/TreeMultiSet.java b/treemultiset/src/main/java/TreeMultiSet.java new file mode 100644 index 0000000..dc3bedd --- /dev/null +++ b/treemultiset/src/main/java/TreeMultiSet.java @@ -0,0 +1,473 @@ +import java.util.*; + +/** + * 允许重复元素的TreeSet + * + * @author: yry + * @date: 2020/3/15 + */ +public class TreeMultiSet extends AbstractSet + implements NavigableSet, Cloneable, java.io.Serializable { + + private static final long serialVersionUID = 1L; + + private transient NavigableMap treeMap; + + // 总个数(重复算多个) + private transient int size = 0; + + private TreeMultiSet(NavigableMap m) { + this.treeMap = m; + } + + public TreeMultiSet() { + this(new TreeMap()); + } + + public TreeMultiSet(Comparator comparator) { + this(new TreeMap<>(comparator)); + } + + public TreeMultiSet(Collection c) { + this(); + addAll(c); + } + + public TreeMultiSet(SortedSet s) { + this(s.comparator()); + addAll(s); + } + + /** + * 可重复元素的迭代器 + */ + private class Itr implements Iterator { + private int cursor; + // 当前treeMap的key对应的 + private Iterator curKeyItr; + // 当前元素 + private E curVal; + // 当前treeMap的key对应的索引,即对应当前第几个重复元素 + private int curKeyItrIndex; + + Itr(NavigableSet keySet) { + cursor = 0; + curKeyItr = keySet.iterator(); + if (curKeyItr.hasNext()) { + curVal = curKeyItr.next(); + } + curKeyItrIndex = 0; + } + + Itr() { + this(treeMap.navigableKeySet()); + } + + @Override + public boolean hasNext() { + return cursor < size; + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + int curValCount = treeMap.get(curVal); + E res = null; + if (curKeyItrIndex < curValCount) { + curKeyItrIndex++; + res = curVal; + if (curKeyItrIndex == curValCount) { + // 移动到下一个key + if (curKeyItr.hasNext()) { + curVal = curKeyItr.next(); + } + curKeyItrIndex = 0; + } + } + + cursor++; + return res; + } + } + + /** + * 返回所有不相同的元素的正向迭代器 + * @return 所有不相同的元素的正向迭代器 + */ + public Iterator diffIterator() { + return treeMap.navigableKeySet().iterator(); + } + + /** + * 返回所有不相同的元素的正向迭代器 + * @return 所有不相同的元素的正向迭代器 + */ + public Iterator diffDescendingIterator() { + return treeMap.descendingKeySet().iterator(); + } + + /** + * 返回所有元素(重复元素要next多次)的正向迭代器 + * @return 所有元素的正向正向迭代器 + */ + @Override + public Iterator iterator() { + return new Itr(); + } + + /** + * 返回所有元素(重复元素要next多次)的反向迭代器 + * @return 所有元素的反向迭代器 + */ + @Override + public Iterator descendingIterator() { + return new Itr(treeMap.descendingKeySet()); + } + + @Override + public NavigableSet descendingSet() { + TreeMultiSet descendingSet = new TreeMultiSet<>(treeMap.descendingMap()); + descendingSet.size = size; + return descendingSet; + } + + /** + * 计算map中的所有元素个数之和(并不是key的个数,而是sum(key * value)) + * @param countMap key为元素,value为count的map + * @return map的所有元素个数之和 + */ + private int calcMapSize(NavigableMap countMap) { + int size = 0; + for (E e : countMap.keySet()) { + size += countMap.get(e); + } + return size; + } + + @Override + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + NavigableMap subMap = treeMap.subMap(fromElement, fromInclusive, toElement, toInclusive); + TreeMultiSet subSet = new TreeMultiSet<>(subMap); + subSet.size = calcMapSize(subMap); + return subSet; + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + NavigableMap headMap = treeMap.headMap(toElement, inclusive); + TreeMultiSet headSet = new TreeMultiSet<>(headMap); + headSet.size = calcMapSize(headMap); + return headSet; + } + + @Override + public NavigableSet tailSet(E fromElement, boolean inclusive) { + NavigableMap tailMap = treeMap.tailMap(fromElement, inclusive); + TreeMultiSet tailSet = new TreeMultiSet<>(tailMap); + tailSet.size = calcMapSize(tailMap); + return tailSet; + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public SortedSet headSet(E toElement) { + return headSet(toElement, false); + } + + @Override + public SortedSet tailSet(E fromElement) { + return tailSet(fromElement, true); + } + + @Override + public Comparator comparator() { + return treeMap.comparator(); + } + + /** + * 返回总的元素个数(重复算多个) + * @return 总的元素个数 + */ + public int size() { + return size; + } + + /** + * 返回不同的元素个数。 + * @return 不同的元素个数 + */ + public int diffElementSize() { + // 也就是treeMap的key的个数 + return treeMap.keySet().size(); + } + + /** + * 获取第一个元素(如果比较器是从小到大的,第一个元素就是最小的,否则就是最大的) + * @return 第一个元素 + */ + public E first() { + if (treeMap.isEmpty()) { + return null; + } + + return treeMap.firstKey(); + } + + /** + * 获取最后元素(如果比较器是从小到大的,第一个元素就是最大的,否则就是最小的) + * @return 最后一个元素 + */ + public E last() { + if (treeMap.isEmpty()) { + return null; + } + return treeMap.lastKey(); + } + + /** + * 是否包含某个元素 + * @param o 指定元素 + * @return true-包含,false-不包含 + */ + @Override + public boolean contains(Object o) { + return treeMap.containsKey(o); + } + + @Override + public void clear() { + treeMap.clear(); + size = 0; + } + + /** + * 添加指定元素(1个) + * @param e 要添加的元素 + * @return true + */ + @Override + public boolean add(E e) { + return add(e, 1); + } + + /** + * 添加指定个数的元素,如果当前元素已经存在,则当前元素的数量+count,否则新建一个key。 + * @param e 要添加的元素 + * @param count 要添加的指定元素的个数 + */ + public boolean add(E e, int count) { + if (treeMap.containsKey(e)) { + treeMap.put(e, treeMap.get(e) + count); + } else { + treeMap.put(e, count); + } + size += count; + return true; + } + + /** + * 设置指定元素的数量 + * @param e 指定元素 + * @param count 数量 + */ + public void setCount(E e, int count) { + int oldCount = treeMap.getOrDefault(e, 0); + treeMap.put(e, count); + size = size - oldCount + count; + } + + /** + * 获取到指定元素的个数 + * @param e 指定元素 + * @return 指定元素的个数 + */ + public int count(E e) { + return treeMap.getOrDefault(e, 0); + } + + /** + * 注意:一定要复写这个方法,目的是覆盖父类Collection的remove操作(复杂度是O(n)) + * 移除1个指定元素 + * @param e 要移除的元素 + * @return 是否移除成功,若移除的元素不存在,返回false, 若移除的元素存在,但是要移除的count大于存在的count,返回false。否则返回true + */ + public boolean remove(Object e) { + return remove((E) e, 1); + } + + /** + * 移除count个指定元素,比如集合为[2,3,3,3,2], 若调用removeCount(3,2),也就是移除2两个2,那么集合就变成[2,3,2] + * @param e 要移除的元素 + * @param count 要移除的指定元素的个数 + * @return 是否移除成功,若移除的元素不存在,返回false, 若移除的元素存在,但是要移除的count大于存在的count,返回false。否则返回true + */ + public boolean remove(E e, int count) { + if (count <= 0) { + return false; + } + + if (!treeMap.containsKey(e)) { + return false; + } + + int curCount = treeMap.get(e); + if (curCount < count) { + return false; + } else if (curCount == count) { + treeMap.remove(e); + } else { + treeMap.put(e, curCount - count); + } + + size -= count; + return true; + } + + /** + * 移除指定元素(包括所有的指定元素)。比如集合为[2,3,3,3,2],若调用removeKey(3),那么会移除所有的3,则集合变成[2,2] + * @param e 要移除的元素 + * @return 是否移除成功,若移除的元素不存在,返回false,否则返回true + */ + public boolean removeAll(Object e) { + if (!treeMap.containsKey(e)) { + return false; + } + + size -= treeMap.get(e); + treeMap.remove(e); + return true; + } + + /** + * 返回比给定元素严格小的最大元素 + * @param e 给定元素 + * @return 比给定元素严格小的最大元素,若不存在,则返回null + */ + @Override + public E lower(E e) { + return treeMap.lowerKey(e); + } + + /** + * 返回小于或等于给定元素的最大元素 + * @param e 给定元素 + * @return 小于或等于给定元素的最大元素,若不存在,则返回null + */ + @Override + public E floor(E e) { + return treeMap.floorKey(e); + } + + /** + * 返回比给定元素严格大的最小元素 + * @param e 给定元素 + * @return 比给定元素严格小的最大元素,若不存在,则返回null + */ + @Override + public E higher(E e) { + return treeMap.higherKey(e); + } + + /** + * 返回大于或等于给定元素的最小元素 + * @param e 给定元素 + * @return 大于或等于给定元素的最小元素,若不存在,则返回null + */ + @Override + public E ceiling(E e) { + return treeMap.ceilingKey(e); + } + + /** + * 删除第一个元素(指定数量) + * @param count 指定数量 + * @return 第一个元素 + */ + public E pollFirst(int count) { + if (treeMap.isEmpty()) { + return null; + } + E firstKey = treeMap.firstKey(); + remove(firstKey, count); + return firstKey; + } + + /** + * 移除第一个元素(第一个元素有多个重复,仅删除其中一个) + * @return 第一个元素 + */ + public E pollFirst() { + return pollFirst(1); + } + + /** + * 移除第一个元素(包括所有数量) + * @return 第一个元素 + */ + public E pollFirstAll() { + // firstKey如果不存在可能会抛异常 + if (treeMap.isEmpty()) { + return null; + } + + E firstKey = treeMap.firstKey(); + size -= treeMap.get(firstKey); + treeMap.remove(firstKey); + return firstKey; + } + + /** + * 删除最后一个元素(若最后一个元素有多个重复,仅删除其中一个) + * @return 最后一个元素 + */ + public E pollLast() { + return pollLast(1); + } + + /** + * 删除最后一个元素(指定数量) + * @param count 指定数量 + * @return 最后一个元素 + */ + public E pollLast(int count) { + if (treeMap.isEmpty()) { + return null; + } + E lastKey = treeMap.lastKey(); + remove(lastKey, count); + return lastKey; + } + + /** + * 移除最后一个元素(包括所有数量) + * @return 最后一个元素 + */ + public E pollLastAll() { + if (treeMap.isEmpty()) { + return null; + } + E lastKey = treeMap.lastKey(); + size -= treeMap.get(lastKey); + treeMap.remove(lastKey); + return lastKey; + } + + /** + * 浅拷贝 + * @return 浅拷贝后的TreeMultiSet + */ + @SuppressWarnings("unchecked") + public Object clone() throws CloneNotSupportedException { + TreeMultiSet clone = (TreeMultiSet) super.clone(); + clone.treeMap = new TreeMap<>(treeMap); + return clone; + } + +} diff --git a/treemultiset/src/test/java/TreeMultiSetTest.java b/treemultiset/src/test/java/TreeMultiSetTest.java new file mode 100644 index 0000000..5c17fdd --- /dev/null +++ b/treemultiset/src/test/java/TreeMultiSetTest.java @@ -0,0 +1,1144 @@ +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.*; + +import static org.junit.Assert.*; + +public class TreeMultiSetTest { + + /** + * 测试是否成功new了一个TreeMap。(因为TreeMultiSet需要基于TreeMap的) + * public TreeMultiSet() { + * this(new TreeMap()); + * } + */ + @Test + public void constructor1() { + TreeMultiSet set = new TreeMultiSet<>(); + try { + Class clazz = Class.forName(TreeMultiSet.class.getName()); + Field treeMapField = clazz.getDeclaredField("treeMap"); + treeMapField.setAccessible(true); + TreeMap treeMap = (TreeMap) treeMapField.get(set); + assertNotNull("构造函数有误,没有new TreeMap", treeMap); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * 测试传入的比较器是否生效 + * TreeMultiSet(Comparator comparator) { + * this(new TreeMap<>(comparator)); + * } + */ + @Test + public void constructor2() { + TreeMultiSet set = new TreeMultiSet<>((o1, o2) -> o2 - o1); // 传一个从大到小的比较器 + set.add(2); + set.add(1); + set.add(2); + + int[] expectedRes = new int[]{2, 2, 1}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * 测试传入的Collection是否生效 + * TreeMultiSet(Collection c) { + * this(); + * addAll(c); + * } + */ + @Test + public void constructor3() { + List list = new ArrayList<>(); + list.add(2); + list.add(1); + list.add(2); + TreeMultiSet set = new TreeMultiSet<>(list); // 直接将已有部分元素的list传入 + + int[] expectedRes = new int[]{1, 2, 2}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * 测试传入的SortedSet是否生效, 如传入treeSet, 或者TreeMultiSet本身 + * TreeMultiSet(SortedSet s) { + * this(s.comparator()); + * addAll(s); + * } + */ + @Test + public void constructor4() { + SortedSet multiSet = new TreeMultiSet<>(); + multiSet.add(1); + multiSet.add(2); + multiSet.add(1); +// Set treeSet = new TreeSet<>(); +// treeSet.add(2); +// treeSet.add(1); + TreeMultiSet set = new TreeMultiSet<>(multiSet); // 直接将已有部分元素的list传入 + + int[] expectedRes = new int[]{1, 1, 2}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void diffIterator() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + Iterator diffIterator = set.diffIterator(); + int[] expectedRes = new int[]{1, 2, 3}; + int[] actualRes = new int[3]; + int i = 0; + while (diffIterator.hasNext()) { + actualRes[i++] = diffIterator.next(); + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void diffDescendingIterator() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + Iterator diffDescendingIterator = set.diffDescendingIterator(); + int[] expectedRes = new int[]{3, 2, 1}; + int[] actualRes = new int[3]; + int i = 0; + while (diffDescendingIterator.hasNext()) { + actualRes[i++] = diffDescendingIterator.next(); + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void iteratorNextException() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + Iterator iterator = set.iterator(); + while (iterator.hasNext()) { + iterator.next(); + } + + try { + iterator.next(); + } catch (Exception e) { + assertTrue(e instanceof NoSuchElementException); + } + } + + @Test + public void iterator() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + Iterator iterator = set.iterator(); + int[] expectedRes = new int[]{1, 2, 2, 3}; + int[] actualRes = new int[4]; + int i = 0; + while (iterator.hasNext()) { + actualRes[i++] = iterator.next(); + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void descendingIterator() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + Iterator descendingIterator = set.descendingIterator(); + int[] expectedRes = new int[]{3, 2, 2, 1}; + int[] actualRes = new int[4]; + int i = 0; + while (descendingIterator.hasNext()) { + actualRes[i++] = descendingIterator.next(); + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void descendingSet() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet descendingSet = (TreeMultiSet) set.descendingSet(); + int[] expectedRes = new int[]{3, 2, 2, 1}; + int[] actualRes = new int[4]; + int i = 0; + for (Integer num : descendingSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * public SortedSet subSet(E fromElement, E toElement) { + * return subSet(fromElement, true, toElement, false); + * } + */ + @Test + public void subSetReturnSortedSet() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet subSet = (TreeMultiSet) set.subSet(2, 4); // 不包含4 + int[] expectedRes = new int[]{2, 2, 3}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : subSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) + * fromInclusive = true, toInclusive = true + */ + @Test + public void subSet1() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet subSet = (TreeMultiSet) set.subSet(2, true, 3, true); + int[] expectedRes = new int[]{2, 2, 3}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : subSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) + * fromInclusive = true, toInclusive = false + */ + @Test + public void subSet2() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet subSet = (TreeMultiSet) set.subSet(2, true, 3, false); + int[] expectedRes = new int[]{2, 2}; + int[] actualRes = new int[2]; + int i = 0; + for (Integer num : subSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) + * fromInclusive = false, toInclusive = true + */ + @Test + public void subSet3() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet subSet = (TreeMultiSet) set.subSet(2, false, 3, true); + int[] expectedRes = new int[]{3}; + int[] actualRes = new int[1]; + int i = 0; + for (Integer num : subSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) + * fromInclusive = false, toInclusive = false + */ + @Test + public void subSet4() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet subSet = (TreeMultiSet) set.subSet(1, false, 6, false); + int[] expectedRes = new int[]{2, 2, 3, 4, 4}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : subSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void headSet1() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet headSet = (TreeMultiSet) set.headSet(3, true); + int[] expectedRes = new int[]{1, 2, 2, 3}; + int[] actualRes = new int[4]; + int i = 0; + for (Integer num : headSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * public SortedSet headSet(E toElement) { + * return headSet(toElement, false); + * } + */ + @Test + public void headSetReturnSortedSet() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet headSet = (TreeMultiSet) set.headSet(3); + int[] expectedRes = new int[]{1, 2, 2}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : headSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void headSet2() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet headSet = (TreeMultiSet) set.headSet(3, false); + int[] expectedRes = new int[]{1, 2, 2}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : headSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void tailSeReturnSortedSet() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet tailSet = (TreeMultiSet) set.tailSet(2); + int[] expectedRes = new int[]{2, 2, 3, 4, 4}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : tailSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void tailSe1() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet tailSet = (TreeMultiSet) set.tailSet(2, true); + int[] expectedRes = new int[]{2, 2, 3, 4, 4}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : tailSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void tailSe2() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(4); + set.add(4); + set.add(2); + set.add(1); + set.add(2); + set.add(3); + + TreeMultiSet tailSet = (TreeMultiSet) set.tailSet(2, false); + int[] expectedRes = new int[]{3, 4, 4}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : tailSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void comparator() { + TreeMultiSet treeMultiSet = new TreeMultiSet<>((o1, o2) -> o2 - o1); + Comparator comparator = treeMultiSet.comparator(); + Comparator newCmp = comparator.reversed(); // 修改方向,原本倒序,reverse之后又变成正序, 注意不是原地修改 + TreeMultiSet treeMultiSet1 = new TreeMultiSet<>(newCmp); + treeMultiSet1.add(2); + treeMultiSet1.add(1); + + int[] expectedRes = new int[]{1, 2}; + int[] actualRes = new int[2]; + int i = 0; + for (Integer num : treeMultiSet1) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void size() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int expectedSize = 5; + int actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void diffElementSize() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int expectedSize = 3; + int actualSize = set.diffElementSize(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void firstNull() { + TreeMultiSet set = new TreeMultiSet<>(); + assertNull(set.first()); + } + + @Test + public void first1() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int expectedFirst = 1; + int actualFirst = set.first(); + assertEquals(expectedFirst, actualFirst); + } + + @Test + public void first2() { + TreeMultiSet set = new TreeMultiSet<>((o1, o2) -> o2 - o1); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int expectedFirst = 3; + int actualFirst = set.first(); + assertEquals(expectedFirst, actualFirst); + } + + @Test + public void lasdtNull() { + TreeMultiSet set = new TreeMultiSet<>(); + assertNull(set.last()); + } + + @Test + public void last1() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int expectedLast = 3; + int actualLast = set.last(); + assertEquals(expectedLast, actualLast); + } + + @Test + public void last2() { + TreeMultiSet set = new TreeMultiSet<>((o1, o2) -> o2 - o1); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int expectedLast = 1; + int actualLast = set.last(); + assertEquals(expectedLast, actualLast); + } + + @Test + public void contains() { + TreeMultiSet set = new TreeMultiSet<>((o1, o2) -> o2 - o1); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + +// boolean expectedRes = false; +// boolean actualRes = set.contains(4); + boolean expectedRes = true; + boolean actualRes = set.contains(3); + assertEquals(expectedRes, actualRes); + } + + @Test + public void clear() { + TreeMultiSet set = new TreeMultiSet<>((o1, o2) -> o2 - o1); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + System.out.println("clear之前"); + for (Integer num : set) { + System.out.print(num + " "); + } + System.out.println(); + + boolean expectedRes = true; + set.clear(); + boolean actualRes = set.size() == 0 && set.diffElementSize() == 0 && set.isEmpty(); + System.out.println("clear之后"); + for (Integer num : set) { + System.out.println(num); + } + assertEquals(expectedRes, actualRes); + } + + @Test + public void addOneCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + int[] expectedRes = new int[]{1, 1, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void addSomeCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2, 3); + set.add(1, 1); + set.add(3, 0); + set.add(3, 2); + + int[] expectedRes = new int[]{1, 2, 2, 2, 3, 3}; + int[] actualRes = new int[6]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void setCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.setCount(2, 3); + set.add(3, 2); + + int[] expectedRes = new int[]{2, 2, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void count() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.setCount(2, 3); + set.add(3); + set.add(2); + + int expectedCount = 4; + int actualCount = set.count(2); + assertEquals(expectedCount, actualCount); + } + + @Test + public void removeOneCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + set.remove(3); + int[] expectedRes = new int[]{1, 1, 2, 3}; + int[] actualRes = new int[4]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void removeSomeCountNotOk() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + boolean expectedIsOk = false; // 删除的数量超过已有的数量 + boolean actualIsOk = set.remove(3, 3); + assertEquals(expectedIsOk, actualIsOk); + int[] expectedRes = new int[]{1, 1, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void removeSomeCountOk() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + boolean expectedIsOk = true; // 删除的数量超过已有的数量 + boolean actualIsOk = set.remove(3, 1); + assertEquals(expectedIsOk, actualIsOk); + int[] expectedRes = new int[]{1, 1, 2, 3}; + int[] actualRes = new int[4]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void removeZeroCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + boolean expectedIsOk = false; // 删除的数量超过已有的数量 + boolean actualIsOk = set.remove(3, 0); + assertEquals(expectedIsOk, actualIsOk); + int[] expectedRes = new int[]{1, 1, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void removeNotContainElement() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + boolean expectedIsOk = false; // 删除的数量超过已有的数量 + boolean actualIsOk = set.remove(5, 1); + assertEquals(expectedIsOk, actualIsOk); + int[] expectedRes = new int[]{1, 1, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + + /** + * 删除所有的指定元素,如2有三个,则把3个2全删了, 执行的就是treeMap的remove key的操作 + */ + @Test + public void removeAllCountOk() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + boolean expectedIsOk = true; + boolean actualIsOk = set.removeAll(1); + assertEquals(expectedIsOk, actualIsOk); + + int[] expectedRes = new int[]{2, 3, 3}; + int[] actualRes = new int[3]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + /** + * 删除所有的指定元素,如2有三个,则把3个2全删了, 执行的就是treeMap的remove key的操作 + */ + @Test + public void removeAllCountNotOk() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + boolean expectedIsOk = false; + boolean actualIsOk = set.removeAll(4); + assertEquals(expectedIsOk, actualIsOk); + + int[] expectedRes = new int[]{1, 1, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : set) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } + + @Test + public void lower() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + Integer expectedLower = 2; + Integer actualLower = set.lower(3); + assertNotNull(actualLower); + assertEquals(expectedLower, actualLower); + + Integer expectedLower1 = 3; + Integer actualLower1 = set.lower(4); + assertNotNull(actualLower1); + assertEquals(expectedLower1, actualLower1); + + Integer expectedLower2 = null; + Integer actualLower2 = set.lower(1); + assertEquals(expectedLower2, actualLower2); + } + + @Test + public void floor() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + Integer expectedFloor = 2; + Integer actualFloor = set.floor(2); + assertNotNull(actualFloor); + assertEquals(expectedFloor, actualFloor); + + Integer expectedFloor1 = 1; + Integer actualFloor1 = set.floor(1); + assertNotNull(actualFloor1); + assertEquals(expectedFloor1, actualFloor1); + + Integer expectedFloor2 = null; + Integer actualFloor2 = set.floor(0); + assertEquals(expectedFloor2, actualFloor2); + } + + @Test + public void higher() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + Integer expectedHigher = 2; + Integer actualHigher = set.higher(1); + assertNotNull(actualHigher); + assertEquals(expectedHigher, actualHigher); + + Integer expectedHigher1 = 1; + Integer actualHigher1 = set.higher(0); + assertNotNull(actualHigher1); + assertEquals(expectedHigher1, actualHigher1); + + Integer expectedHigher2 = null; + Integer actualHigher2 = set.higher(4); + assertEquals(expectedHigher2, actualHigher2); + } + + @Test + public void ceiling() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + Integer expectedCeiling = 2; + Integer actualCeiling = set.ceiling(2); + assertNotNull(actualCeiling); + assertEquals(expectedCeiling, actualCeiling); + + Integer expectedCeiling1 = 3; + Integer actualCeiling1 = set.ceiling(3); + assertNotNull(actualCeiling); + assertEquals(expectedCeiling1, actualCeiling1); + + Integer expectedCeiling2 = null; + Integer actualCeiling2 = set.ceiling(4); + assertEquals(expectedCeiling2, actualCeiling2); + } + + @Test + public void pollFirstSomeCountEmptySet() { + TreeMultiSet set = new TreeMultiSet<>(); + + // 当前first 有3个1 + Integer expectedRes = null; + Integer actualRes = set.pollFirst(3); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 0; + Integer actualLeftCount = set.count(1); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 0; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollFirstSomeCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + set.add(1); + + // 当前first 有3个1 + Integer expectedRes = 1; + Integer actualRes = set.pollFirst(2); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 1; + Integer actualLeftCount = set.count(1); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 4; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollFirstOneCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + set.add(1); + + // 当前first 有3个1 + Integer expectedRes = 1; + Integer actualRes = set.pollFirst(); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 2; + Integer actualLeftCount = set.count(1); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 5; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollFirstAllCountNull() { + TreeMultiSet set = new TreeMultiSet<>(); + assertNull(set.pollFirstAll()); + } + + @Test + public void pollFirstAllCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + set.add(1); + + // 当前first 有3个1 + Integer expectedRes = 1; + Integer actualRes = set.pollFirstAll(); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 0; + Integer actualLeftCount = set.count(1); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 3; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollLastSomeCountEmptySet() { + TreeMultiSet set = new TreeMultiSet<>(); + + // 当前first 有3个1 + Integer expectedRes = null; + Integer actualRes = set.pollLast(3); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 0; + Integer actualLeftCount = set.count(1); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 0; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollLastSomeCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + set.add(1); + + // 当前first 有3个1 + Integer expectedRes = 3; + Integer actualRes = set.pollLast(2); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 0; + Integer actualLeftCount = set.count(3); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 4; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollLastOneCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + set.add(1); + + // 当前first 有3个1 + Integer expectedRes = 3; + Integer actualRes = set.pollLast(); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 1; + Integer actualLeftCount = set.count(3); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 5; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void pollLastAllCountNull() { + TreeMultiSet set = new TreeMultiSet<>(); + assertNull(set.pollLastAll()); + } + + @Test + public void pollLastAllCount() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + set.add(1); + + // 当前first 有3个1 + Integer expectedRes = 3; + Integer actualRes = set.pollLastAll(); + assertEquals(expectedRes, actualRes); + + Integer expectedLeftCount = 0; + Integer actualLeftCount = set.count(3); + assertEquals(expectedLeftCount, actualLeftCount); + + Integer expectedSize = 4; + Integer actualSize = set.size(); + assertEquals(expectedSize, actualSize); + } + + @Test + public void testClone() { + TreeMultiSet set = new TreeMultiSet<>(); + set.add(2); + set.add(3); + set.add(1); + set.add(3); + set.add(1); + + TreeMultiSet cloneSet = null; + try { + cloneSet = (TreeMultiSet) set.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + + int[] expectedRes = new int[]{1, 1, 2, 3, 3}; + int[] actualRes = new int[5]; + int i = 0; + for (Integer num : cloneSet) { + actualRes[i++] = num; + } + assertArrayEquals(expectedRes, actualRes); + } +} \ No newline at end of file